while
loopsWe previously used for
loops to execute a block of code for a specific number
of repetitions. What if we don’t know how many iterations are needed? What
might be such a situation? Obtaining a valid input from a user. We don’t know
how many tries it will take someone to provide a valid input.
This is where we apply while
loops.
General structure:
while <bool expression>:
statement1
statement2
...
statementn
The statements in the loop body (i.e., statement1 … statementn) will be executed
repeatedly until the boolean expression, the loop conditional, evaluates to
False
.
Here is a concrete example. What will this code print?
>>> x = 5
>>> while x > 0:
... print(x)
... x = x-1
...
5
4
3
2
1
What is the implication of while
loops? That some statement(s) within the
body of the while
loop will change the loop conditional (or otherwise
terminate the loop). What happens if that is not the case, e.g.,
while True:
print("How many times will this string get printed?")
Hit Ctrl-C (Ctrl and C simultaneously) to stop execution.
This is called an infinite loop and is a common problem. You will likely need to use Ctrl-C at some point.
What about the following loop? Will it terminate?
i = 0
while i < 10:
print("How many times will this string get printed?")
i = i - 1
No. Because i
starts at 0 and only gets smaller it will always be less than 10.
Consider the following loop – will it terminate?
i = randint(1, 20)
while i < 10:
print("How many times will this string get printed?")
i = i - 1
It depends. If the value of i is less than 10, the loop will run infinitely. If the value is greater than or equal to 10, the loop won’t execute at all.
In addition to changing the loop conditional we can also explicitly terminate
the loop with the break
statement. As its name suggests, break
terminates
the loop and begins executing the first statement after the loop.
Peer instruction questions (While loops) [1] (Section A, Section B)
Let’s look at a more complex example: Let’s implement a guessing game in which Python picks a random integer from 1-20 (inclusive), and keeps asking the user to guess the number until they get it right. To help the user, the game should give hints “higher”, or “lower”.
What would be some key elements in such a program?
Check out guessing-game.py.
Here we see 3 strategies for implementing the loop:
correct
to track if the user answered correctlybreak
out of a “while True” loop on a correct answerAs an aside, the last simulates the “do-while loop” semantics available in other languages.
Note the use of the input
function for reading user inputs:
>>> help(input)
Help on built-in function input in module builtins:
input(prompt=None, /)
Read a string from standard input. The trailing newline is stripped.
The prompt string, if given, is printed to standard output without a
trailing newline before reading input.
If the user hits EOF (*nix: Ctrl-D, Windows: Ctrl-Z+Return), raise EOFError.
On *nix systems, readline is used if available.
input
returns a string and so we need to convert the result to an int
for
use in our game.
Often we can implement the same functionality with both a for
loop
and a while
loop. In fact, in Python any for
loop can be readily
implemented with a while
loop. The reverse is trickier and not
always possible, so for our purposes we should think of while
loops as
a superset of for
loops.
So when do we use a for
loop and when do we use a while
loop?
We use a for
loop when
As an example, iterating over all the elements in a sequence, e.g., a string, is an example of a situation where the number of iterations is known at the initiation of the loop (number of elements in sequence) and the increment is consistent (increment one element each iteration).
We would use a while
loop in other settings, such as
This an example where “style” matters but there are not necessarily clear “rules”. Often one approach or the other is more appropriate. The right choice will make the code more elegant, easier to reason about (and easier to debug).
Will the following print statement get executed?
if "a string?":
print("Do I get executed?")
Yes. Most values can be used in a boolean context. In Python, 0
, 0.0
,
None
, empty sequences (e.g., “”) and a few other values evaluate as False
and everything else is True
.
In general, using this “implicit” type conversion is not recommended as it just increases the chances for difficult-to-find bugs.
Now that we know about implicit booleans, though, we can resolve a common
bug. In the Socrative question, the solution was a == b or a == 5
. Can we
simplify that expression as a == b or 5
? No, that expression is evaluated
as (a == b) or 5
, which is not the same (and will always evaluate to True
as 5
evaluates as True
).
Let’s think about a and b
. If a
evaluates to False
do I need to evaluate
b
? Similarly if in a or b
a
evaluates to True
do I need to evaluate
b
?
No.
Python can “short-circuit” the evaluation. Doing so is a very powerful technique for managing potentially problematic situations.
is_valid(input) and dangerous_operation(input)
We can also apply relational operators, i.e., <
, ==
, etc., to other types;
most notably strings. For example:
>>> 'Aardvark' < 'Zebra'
True
>>> 'aardvark' < 'Zebra'
False
What is happening in these two comparisons?
For strings, a comparison operator such as <
implements lexicographic ordering, i.e., it compares the ordering of
corresponding characters. The first characters are compared, if equal, then the
2nd characters are compared and so on. If one string is a substring of the
other, it is lexicographically less than, e.g.,
>>> "abc" < "abcdef"
True
This is not the same as a case-insensitive alphabetical ordering. In the character encoding used by Python (and lots of other software), all upper case letters are less than lower case letters. Hence “aardvark” is “greater than” “Zebra”.
Some more examples:
>>> "test" == "test"
True
>> "test" == "testing"[0:4]
True
>> "test" == "TEST"
False
If we wanted to ensure that a string comparison was case insensitive, how could
be we do so? Use the upper
or lower
methods to ensure consistent case.
while
loops