Iteration

City University of Hong Kong

CS1302 Introduction to Computer Programming


%reload_ext divewidgets

from ipywidgets import interact

Motivation

Many tasks are repetitive. For examples:

  • 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?

num = int(input(">"))
if 1 <= num:
    print(1)
if 2 <= num:
    print(2)
if 3 <= num:
    print(3)
### BEGIN SOLUTION
for i in range(4, num+1):
    print(i)
### END SOLUTION
Important

Code duplication is a bad practice:

  • 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 loop as follows:

%%optlite -h 300
for i in 1, 2, 3, 4:
    print(i)
  • 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.

for loop can also use tuple assignment to iterate over different objects:

tuples = (0, "l"), (1, "o"), (2, "o"), (3, "p")
for i, c in tuples:
    print(i, c)

With iterable object, one can write short and easily understandable code:

for i, c in enumerate("loop"):
    print(i, c)

Iterate over a range

How to print up to a user-specified number?

One way is to use range:

stop = int(input(">")) + 1
for i in range(stop):
    print(i)

How to start from a number different from 0?

for i in range(1, 5):
    print(i)

What about a step size different from 1?

for i in range(0, 5, 2):
    print(i)  # starting number must also be specified. Why?
### BEGIN SOLUTION
for i in range(4, -1, -1):
    print(i)
### END SOLUTION
num = int(input(">"))
### BEGIN SOLUTION
for i in range(0, 2 * num + 1, 1):
    print(i / 2)
### END SOLUTION
@interact(m=(0, 10), n=(0, 10))
def draw_rectangle(m=5, n=5):
    ### BEGIN SOLUTION
    for i in range(m):
        for j in range(n):
            print("*", end="")
        print()
    ### END SOLUTION

Iterate over a string

What does the following do?

%%optlite -h 300
for character in "loop":
    print(character)
message = "loop"
print("length:", len(message))
print("characters:", message[0], message[1], message[2], message[3])

We can also iterate over a string as follows although it is less elegant:

for i in range(len("loop")):
    print("loop"[i])
@interact(message="loop")
def reverse_print(message):
    ### BEGIN SOLUTION
    for i in range(len(message)):
        print(message[-i - 1], end="")
    print("")  # in case message is empty
    ### END SOLUTION

While Loop

How to ensure user input is non-empty?

Python provides the while statement to loop until a specified condition is false.

while not input("Input something please:"):
    pass

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, and so
  • the looping condition not input('...') is True.

Is it possible to use a for loop instead of a while loop?

definite vs indefinite loops

  • 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 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.

i = 0
while i <= 4:
    print(i)
    i += 1

A while loop may not be as elegant, c.f.,

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.

num = int(input(">"))
i = 0
while i != num + 1:
    print(i)
    i += 1
for i in range(int(input(">")) + 1):
    print(i)
Solution to Exercise 6

When the input corresponds to an integer 2\leq -2,

  • the while loop becomes an infinite loop, but
  • the for loop terminates without printing any number.

Break/Continue/Else Constructs of a Loop

Breaking out of a loop

Is the following an infinite loop?

while True:
    message = input("Input something please:")
    if message:
        break
print("You entered:", message)

The loop is terminated by the break statement when user input is non-empty.

Why is the break statement useful?

Recall the earlier while loop:

while not input("Input something please:"):
    pass

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:

has_no_input = True
while has_no_input:
    message = input("Input something please:")
    if message:
        has_no_input = False
print("You entered:", message)

Continue to Next Iteration

What does the following program do?
Is it an infinite loop?

while True:
    message = input("Input something please:")
    if not message:
        continue
    print("You entered:", message)
  • 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.
while True:
    message = input("Input something please:")
    ### BEGIN SOLUTION
    if message:
        print("You entered:", message)
    ### END SOLUTION

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.
@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?
Solution to Exercise 8
  • 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 execution:

%%optlite -h 650
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")

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, i.e., not by a break.

@interact(num="1")
def check_composite(num):
    if num.isdigit():
        num = int(num)
        ### BEGIN SOLUTION
        # for divisor in range(2,num):
        divisor = int(num**0.5)  # biggest possible divisor
        while divisor > 1:
            if num % divisor:
                divisor -= 1  # why not go from 2 to int(num ** 0.5)?
            else:
                print("It is composite.")
                break
        else:
            print("It is not composite.")
        ### END SOLUTION
    else:
        print("Not a positive integer.")