Iteration¶
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?
%%mytutor -h 400
num = int(input(">"))
if 1 <= num:
print(1)
if 2 <= num:
print(2)
if 3 <= num:
print(3)
# YOUR CODE HERE
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 as follows:
%%mytutor -h 300
for i in 1, 2, 3, 4:
print(i)
iis automatically assigned to each element in the sequence1, 2, 3, 4one-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 instead.
tuples = (0, "l"), (1, "o"), (2, "o"), (3, "p")
for i, c in tuples:
print(i, c)
0 l
1 o
2 o
3 p
An even shorter code…
for i, c in enumerate("loop"):
print(i, c)
0 l
1 o
2 o
3 p
Iterate over a range¶
How to print up to a user-specified number?
We can use range:
%%mytutor -h 300
stop = int(input(">")) + 1
for i in range(stop):
print(i)
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?
for i in range(1, 5):
print(i)
1
2
3
4
What about a step size different from 1?
for i in range(0, 5, 2): print(i) # starting number must also be specified. Why?
0
2
4
Exercise How to count down from 4 to 0? Try doing it without addition or subtraction.
### BEGIN SOLUTION
for i in range(4, -1, -1):
print(i)
### END SOLUTION
4
3
2
1
0
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.0
0.5
1.0
1.5
2.0
Note: range only accepts integer arguments.
%%mytutor -h 300
num = int(input(">"))
### BEGIN SOLUTION
for i in range(0, 2 * num + 1, 1):
print(i / 2)
### END SOLUTION
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).
@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?
%%mytutor -h 300
for character in "loop":
print(character)
str is a sequence type because a string is regarded as a sequence of characters.
The function
lencan return the length of a string.The indexing operator
[]can return the character of a string at a specified location.
message = "loop"
print("length:", len(message))
print("characters:", message[0], message[1], message[2], message[3])
length: 4
characters: l o o p
We can also iterate over a string as follows although it is less elegant:
for i in range(len("loop")):
print("loop"[i])
l
o
o
p
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.
@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.
%%mytutor -h 300
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,
inputreturns an empty string'', which is regarded asFalse, and sothe looping condition
not input('...')isTrue.
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.
whilestatement 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.
i = 0
while i <= 4:
print(i)
i += 1
0
1
2
3
4
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.
%%mytutor -h 310
num = int(input(">"))
i = 0
while i != num + 1:
print(i)
i += 1
Exercise Is the above while loop doing the same thing as the for loop below?
%%mytutor -h 300
for i in range(int(input(">")) + 1):
print(i)
When the input corresponds to an integer \(\leq -2\),
the while loop becomes an infinite loop, but
the for loop terminates without printing any number.
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?)
Break/Continue/Else Constructs of a Loop¶
Breaking out of a loop¶
Is the following an infinite loop?
%%mytutor -h 310
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:
%%mytutor -h 300
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
breakstatement by using flags, which are boolean variables for flow control:
%%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)
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?
%%mytutor -h 310
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
continuestatement 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?
%%mytutor -h 350
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?
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
2tonum.
If program flow is confusing, try stepping through executation:
%%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")
In addition to using
continueandbreakin 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.
@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.")