Lab 5: Other Early Exits from Loops
In the previous part, we saw how a return from a function can terminate a loop
early (by ending the execution of the function body, which effectively stops
any code executing in the function).
Here we explore Python constructs for changing control flow in a loop
that do not require the return construct or embedding the loop in
a function.
Table of Contents
- The
breakStatement - The
for/elseStatement - The
continueStatement - All Together Now
- A Complex Example
The break statement
In both for and while loops, break means "exit the innermost loop in which
the break appears, and transfer control to the statement following the loop."
Consider this testBreak function:
def testBreak(nums):
for n in nums:
if n > 10:
break
print(n)
print('after for')
The call testBreak([5, -2, 17, -4, 6]) prints
5
-2
after for
because when n is 17, the break causes the for loop to end,
and the next statement after the for loop is executed.
In contrast in the call testBreak([5, -2, -4, 6]), the break statement
is never executed in the loop, so all number in the list are printed,
and and for loop finishes normally when the last number in the list is processed.
5
-2
-4
6
after for
The for/else statement
A problem with break is that it's not easy to tell whether
the statement after the loop is reached because the loop finished
normally or whether it was reached because of a break.
To distinguish these cases, a boolean flag variable
can be introduced to distinguish the two cases,
such as didBreak in the following example:
def testBreakFlag(nums):
def testBreakFlag(nums):
didBreak = False
for n in nums:
if n > 10:
didBreak = True
break
print(n)
if didBreak:
... code to execute in break case ...
else:
... code to execute in normal loop completion case ...
But Python provides a handy special for/else statement that
reduce the need for such a flag:
def testBreakElse(nums):
for n in nums:
if n > 10:
break
print(n)
else:
print('for else (no break)')
print('after for')
In the above example, the print('for else (no break)') is
executed only in the case where break is not executed
during the regular loop body. So testBreakElse([5, -2, 8, -4, 6]),
for which no break is executed, prints
5
-2
8
-4
6
for else (no break)
after for
but testBreakElse([5, -2, 17, -4, 6]), which executes the break
when n is 17, prints only three lines:
5
-2
after for
The syntax of for/else is unnecessarily confusing. In the above example,
the else: does not belong with the if in the body of the
for loop; instead it is part of the for loop itself and
means "execute the else: code only when the loop completed
normally (i.e., no break was executed). It would be better
better if else: were replaced by something like noBreak:
BTW, there is also a corresponding while/else statement for
while loops that behaves analogously .
The continue statement
def testContinue(nums):
for n in nums:
if n < 0:
continue
print(n)
print('after for')
Both for and while loops support a continue statement, which means
"stop executing the statements in the current iteration of the loop,
but start the next iteration of the loop. For example, for the function
def testContinue(nums):
for n in nums:
if n < 0:
continue
print(n)
print('after for')
the call testContinue([5, 2, -3, 17, -1, -4, 6]) prints only the
nonnegative numbers before moving on to the statement after the loop:
5
2
17
6
after for
You might well wonder why
if n < 0:
continue
print(n)
isn't just replaced by
if n >= 0:
print(n)
In this particular case, that would be an easy transformation to make.
But in general, the point at which the realization is made that
it's desirable to punt the current iteration and go to the next one
might be deeply nested in the code and require a complex rewrite of
the existing code in order to implement this behavior. Having a
simple continue statement gets the same effect without having
to rewrite the code!
All Together Now
def testExits(string):
for char in string:
if char.isdigit():
continue
elif char == '!':
break
print(f"in for body: {char}")
else:
print(f"in for else (no break)")
print(f"after for")
Consider the above testExits function. Assess your understanding
of break, continue, and for/else by predicting the
printed output of the following two calls.
predictions by executing the code in Thonny).
-
testExits('a1?b2#4c')Show Answer
in for body: a in for body: ? in for body: b in for body: # in for body: c in for else (no break) after for
-
testExits('a1?b2!4c')Show Answer
in for body: a in for body: ? in for body: b after for
A Complex Example
This section ends with the following complex function that uses nested for/else loops
with break statements. See if you can correctly predict the printed output
of the 4 function calls after the definition.
def findMultiple(nums, n1, n2):
for n in nums:
if n%n1 == 0:
print(f"First multiple of {n1} is {n}")
break
else:
for n in nums:
if n%n2 == 0:
print(f"First multiple of {n2} is {n}")
break
else:
print(f"Found no multiples of {n1} or {n2}")
print('Done with problem.')
-
findMultiple([17, 28, 6, 20, 14], 7, 4)Show Answer
First multiple of 7 is 28 Done with problem.
-
findMultiple([17, 29, 6, 20, 14], 7, 4)Show Answer
First multiple of 7 is 14 Done with problem.
-
findMultiple([17, 29, 6, 20, 13], 7, 4)Show Answer
First multiple of 4 is 20 Done with problem.
-
findMultiple([17, 29, 6, 19, 13], 7, 4)Show Answer
Found no multiples of 7 or 4 Done with problem.
print('-'40) findMultiple([17, 28, 6, 20, 14], 7, 4) print('-'40) findMultiple([17, 29, 6, 20, 14], 7, 4) print('-'40) findMultiple([17, 29, 6, 20, 13], 7, 4) print('-'40) findMultiple([17, 29, 6, 19, 13], 7, 4)
Done with problem.
Done with problem.
Table of Contents
- Lab 5 Home
- Part 1: General Important Style Guidelines
- Part 1: Early Returns from Loops in a Function
- Part 2: Other Early Exits from Loops
- Part 3: Tracking Variables
- Part 4: for-based Index Loops
- Part 5: Advanced
forLoop Exercises - Part 6: Debugging Loops
- Part 7: Loops and Graphics(optional)