<jupyter_start><jupyter_text>Iteration **CS1302 Introduction to Computer Programming**___<jupyter_code>%reload_ext mytutor
from ipywidgets import interact<jupyter_output><empty_output><jupyter_text>Motivation Many tasks are repetitive:- To print from 1 up to a user-specified number *arbitrarily large*.- To compute the maximum of a sequence of numbers *arbitrarily long*.- To get user input *repeatedly until* it is within a certain range. **How to write code to perform repetitive tasks?** E.g., can you complete the following code to print from 1 up to a user-specified number?<jupyter_code>%%mytutor -h 400
num = int(input(">"))
if 1 <= num:
    print(1)
if 2 <= num:
    print(2)
if 3 <= num:
    print(3)
# YOUR CODE HERE<jupyter_output><empty_output><jupyter_text>*Code duplication* is not good because:  - Duplicate code is hard to read/write/maintain.    (Imagine what you need to do to change some code.)- The number of repetitions may not be known before runtime. Instead, programmers write a *loop* which specifies a piece of code to be executed iteratively.  For Loop  Iterate over a sequence **How to print from 1 up to 4?** We can use a [`for` statement](https://docs.python.org/3.3/tutorial/controlflow.htmlfor-statements) as follows:<jupyter_code>%%mytutor -h 300
for i in 1, 2, 3, 4:
    print(i)<jupyter_output><empty_output><jupyter_text>- `i` is automatically assigned to each element in the sequence `1, 2, 3, 4` one-by-one from left to right.- After each assignment, the body `print(i)` is executed. N.b., if `i` is defined before the for loop, its value will be overwritten.   The assignment is not restricted to integers and can also be a tuple assignment. The expression list can also be an [iterable object](https://docs.python.org/3.3/glossary.htmlterm-iterable) instead.<jupyter_code>tuples = (0, "l"), (1, "o"), (2, "o"), (3, "p")
for i, c in tuples:
    print(i, c)<jupyter_output><empty_output><jupyter_text>An even shorter code...<jupyter_code>for i, c in enumerate("loop"):
    print(i, c)<jupyter_output><empty_output><jupyter_text>Iterate over a range **How to print up to a user-specified number?** We can use [`range`](https://docs.python.org/3/library/stdtypes.htmlrange):<jupyter_code>%%mytutor -h 300
stop = int(input(">")) + 1
for i in range(stop):
    print(i)<jupyter_output><empty_output><jupyter_text>**Why add 1 to the user input number?** `range(stop)` generates a sequence of integers from `0` up to *but excluding* `stop`. **How to start from a number different from `0`?**<jupyter_code>for i in range(1, 5):
    print(i)<jupyter_output><empty_output><jupyter_text>**What about a step size different from `1`?**<jupyter_code>for i in range(0, 5, 2): print(i)  # starting number must also be specified. Why?<jupyter_output><empty_output><jupyter_text>**Exercise** How to count down from 4 to 0? Try doing it without addition or subtraction.<jupyter_code># YOUR CODE HERE
raise NotImplementedError()<jupyter_output><empty_output><jupyter_text>**Exercise** Print from `0` to a user-specified number but in steps of `0.5`.  E.g., if the user inputs `2`, the program should print:```0.00.51.01.52.0```*Note:* `range` only accepts integer arguments.<jupyter_code>%%mytutor -h 300
num = int(input(">"))
# YOUR CODE HERE
raise NotImplementedError()<jupyter_output><empty_output><jupyter_text>**Exercise** How to print the character `'*'` repeatedly for `m` rows and `n` columns?  Try using a *nested for loop*: Write a for loop (*inner loop*) inside the body of another for loop (*outer loop*).<jupyter_code>@interact(m=(0, 10), n=(0, 10))
def draw_rectangle(m=5, n=5):
    # YOUR CODE HERE
    raise NotImplementedError()<jupyter_output><empty_output><jupyter_text>Iterate over a string **What does the following do?**<jupyter_code>%%mytutor -h 300
for character in "loop":
    print(character)<jupyter_output><empty_output><jupyter_text>`str` is a [sequence type](https://docs.python.org/3/library/stdtypes.htmltextseq) because a string is regarded as a sequence of characters.- The function [`len`](https://docs.python.org/3/library/functions.htmllen) can return the length of a string.- The indexing operator `[]` can return the character of a string at a specified location.<jupyter_code>message = "loop"
print("length:", len(message))
print("characters:", message[0], message[1], message[2], message[3])<jupyter_output><empty_output><jupyter_text>We can also iterate over a string as follows although it is less elegant:<jupyter_code>for i in range(len("loop")):
    print("loop"[i])<jupyter_output><empty_output><jupyter_text>**Exercise** Print a string assigned to `message` in reverse.  E.g., `'loop'` should be printed as `'pool'`. Try using the for loop and indexing operator.<jupyter_code>@interact(message="loop")
def reverse_print(message):
    # YOUR CODE HERE
    raise NotImplementedError()<jupyter_output><empty_output><jupyter_text>While Loop **How to ensure user input is non-empty?** Python provides the [`while` statement](https://docs.python.org/3/reference/compound_stmts.htmlwhile) to loop until a specified condition is false.<jupyter_code>%%mytutor -h 300
while not input("Input something please:"):
    pass<jupyter_output><empty_output><jupyter_text>As long as the condition after `while` is true, the body gets executed repeatedly. In the above example,- if user inputs nothing, - `input` returns an empty string `''`, which is [regarded as `False`](https://docs.python.org/3/reference/expressions.htmlbooleans), and so- the looping condition `not input('...')` is `True`. **Is it possible to use a for loop instead of a while loop?** - Not without hacks because the for loop is a *definite loop* which has a definite number of iterations before the execution of the loop.- `while` statement is useful for an *indefinite loop* where the number of iterations is unknown before the execution of the loop. It is possible, however, to replace a for loop by a while loop.  E.g., the following code prints from `0` to `4` using a while loop instead of a for loop.<jupyter_code>i = 0
while i <= 4:
    print(i)
    i += 1<jupyter_output><empty_output><jupyter_text>- A while loop may not be as elegant, c.f.,    ```Python  for i in range(5): print(i)  ```- but it can be as efficient. **Should we just use while loop?** Consider using the following while loop to print from `0` to a user-specified value.<jupyter_code>%%mytutor -h 310
num = int(input(">"))
i = 0
while i != num + 1:
    print(i)
    i += 1<jupyter_output><empty_output><jupyter_text>**Exercise** Is the above while loop doing the same thing as the for loop below?<jupyter_code>%%mytutor -h 300
for i in range(int(input(">")) + 1):
    print(i)<jupyter_output><empty_output><jupyter_text>YOUR ANSWER HERE We have to be careful not to create unintended *infinite loops*.  The computer can't always detect whether there is an infinite loop. ([Why not?](https://en.wikipedia.org/wiki/Halting_problem))  Break/Continue/Else Constructs of a Loop  Breaking out of a loop **Is the following an infinite loop?**<jupyter_code>%%mytutor -h 310
while True:
    message = input("Input something please:")
    if message:
        break
print("You entered:", message)<jupyter_output><empty_output><jupyter_text>The loop is terminated by the [`break` statement](https://docs.python.org/3/tutorial/controlflow.htmlbreak-and-continue-statements-and-else-clauses-on-loops) when user input is non-empty. **Why is the `break` statement useful?**  Recall the earlier `while` loop:<jupyter_code>%%mytutor -h 300
while not input("Input something please:"):
    pass<jupyter_output><empty_output><jupyter_text>This while loop is not useful because it does not store the user input. **Is the `break` statement strictly necessary?**  - We can use the assignment expression but it is not supported by Python version <3.8. - We can avoid `break` statement by using *flags*, which are boolean variables for flow control:<jupyter_code>%%mytutor -h 350
has_no_input = True
while has_no_input:
    message = input("Input something please:")
    if message:
        has_no_input = False
print("You entered:", message)<jupyter_output><empty_output><jupyter_text>Using flags makes the program more readable, and we can use multiple flags for more complicated behavior.  The variable names for flags are often `is_...`, `has_...`, etc.  Continue to Next Iteration **What does the following program do?  Is it an infinite loop?**<jupyter_code>%%mytutor -h 310
while True:
    message = input("Input something please:")
    if not message:
        continue
    print("You entered:", message)<jupyter_output><empty_output><jupyter_text>- The program repeatedly asks the user for input.- If the input is empty, the `continue` statement will skip to the next iteration.- The loop can only be terminated by interrupting the kernel.- Such an infinite loop can be useful. E.g., your computer clock continuously updates the current time. **Exercise** Is the `continue` statement strictly necessary? Can you rewrite the above program without the `continue` statement?<jupyter_code>%%mytutor -h 350
while True:
    message = input("Input something please:")
    # YOUR CODE HERE
    raise NotImplementedError()<jupyter_output><empty_output><jupyter_text>Else construct for a loop The following program checks whether a number is composite, namely,  - a positive integer that is- a product of two strictly smaller positive integers.<jupyter_code>@interact(num="1")
def check_composite(num):
    if num.isdigit():
        num = int(num)
        for divisor in range(2, num): # why starts from 2 instead of 1
            if num % divisor:
                continue  # where will this go?
            else:
                print("It is composite.")
                break  # where will this go?
        else:
            print("It is not composite.")  # how to get here?
    else:
        print("Not a positive integer.")  # how to get here?<jupyter_output><empty_output><jupyter_text>**Exercise** There are three else claues in the earlier code. Which one is for the loop? - The second else clause that `print('It is not composite.')`.- The clause is called when there is no divisor found in the range from `2` to `num`. If program flow is confusing, try stepping through executation:<jupyter_code>%%mytutor -h 520
def check_composite(num):
    if num.isdigit():
        num = int(num)
        for divisor in range(2, num):
            if num % divisor:
                continue
            else:
                print("It is composite.")
                break
        else:
            print("It is not composite.")
    else:
        print("Not a positive integer.")


check_composite("1")
check_composite("2")
check_composite("3")
check_composite("4")<jupyter_output><empty_output><jupyter_text>- In addition to using `continue` and `break` in an elegant way,  - the code also uses an else clause that is executed only when the loop terminates *normally* not by `break`. **Exercise** Convert the for loop to a while loop. Try to make the code as efficient as possible with less computation and storage.<jupyter_code>@interact(num="1")
def check_composite(num):
    if num.isdigit():
        num = int(num)
        # YOUR CODE HERE
        raise NotImplementedError()
    else:
        print("Not a positive integer.")<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.11219985875706215, 'fraction_numerical': 0.012358757062146893, 'mean_word_length': 4.488856589147287, 'pattern_counts': {'":': 1, '<': 114, '<?xml version=': 0, '>': 114, 'https://': 9, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 1, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '27', 'dir': 'jupyter-structured-clean-dedup', 'id': '4327', 'license': 'MIT', 'n_tokens_mistral': 3594, 'n_tokens_neox': 3384, 'n_words': 1309, 'path': 'Lecture3/Iteration.ipynb', 'repo_name': 'ccha23/CS1302', 'size': '11328'}