# Loops

Don Miner & Sue Evans

Adapted from the CS1 Course at Swarthmore College by Lisa Meeden

Hit the space bar for next slide

# Learning Outcomes

• Understand that many problems require using repeated code.
• Understand that for loops are used to iterate over sequences.
• Familiarity with using nested for loops for 2 dimensional data.
• Understand while loops, including sentinel loops and post-test loops.
• Familiarity with the terms: priming read, sentinel, continuation condition, termination condition, iteration, and loop control variable.
• Understand reading from files using while loops and for loops.
• Be aware that break and continue damage readability and alter typical program flow.
• Be aware of possible loop control problems due to inaccuracy of floating point numbers and binary storage.

# For Loops

• We've already used the simplest of for loops that iterates a specified amount of times. Recall that it can use the range function and when it does, it has the form:
• ```
for <variable> in range(<iterations>):
<code block>

```
• For loops, when using range, assigns 0, 1, ..., <iterations> - 1 into the <variable>, then executes the code in the<code block> each time.
• The <code block> is a new block, and like in an if statement or a function, it must be indented.
• To count to 5 with a for loop:
• ```
>>> for i in range(6):
...     print i,
...
0 1 2 3 4 5

```
• Instead of using 5, a variable could be substituted:
```
>>> def count(number):
...     for i in range(number + 1):
...         print i,
...
>>> count(15)
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

```

# range

• range assumes you want to start counting at 0. We can change this by giving it two parameters: a start number and a stopping number (plus one).
• Note: range stopping at the number minus one will make more sense when we talk about iterating over lists.
• For example:
• ```
>>> def count(start, stop):
...     for i in range(start, stop + 1):
...         print i,
...
>>> count(11, 15)
11 12 13 14 15

```
• By specifying a third parameter to range, we can control the step (how many to add every time):
• ```
>>> def countBy2(start, stop):
...     for i in range(start, stop + 1, 2):
...         print i,
...
>>> countBy2(11, 15)
11 13 15

```
• We can also specify a negative number in the third parameter of range to count backwards:
```
>>> def countBack(start, stop):
...     for i in range(start, stop - 1, -1):
...         print i,
...
>>> countBack(9, 4)
9 8 7 6 5 4

```

# Example: Factors

• What are the factors of some number, n ?
• Let's loop over all numbers less than n and see if they'll divide evenly into it.
• We'll use the modulus operator % that returns the remainder of a division:
• ```
>>> 5 % 4
1
>>> # will return 0 because 2 divides 10 evenly
... 10 % 2
0
>>> 9 % 5
4

```
• The code:
• ```
>>> def factors(n):
...    for i in range(2, n):
...        if n % i == 0:
...            print i,
...
>>> factors(30)
2 3 5 6 10 15

```

# Iterating over sequences

• For loops can iterate over lists, strings and other sequences.
• We can iterate character by character through a string. Lets add a space between each character:
• ```>>> word = "hello world"
>>> for c in word:
...     print c,
...
h e l l o   w o r l d
```
• Also, we can iterate over elements in a list:
• ```>>> myList = [32, 45, 34, 76, 45]
>>> sum = 0.0
>>> for n in myList:
...    sum += n
...
>>> print sum / len(myList)
46.4
```
• range actually just generates a list:
```>>> print range(1,5)
[1, 2, 3, 4]
```

# Nested For Loops

• For loops can be nested, meaning you can have a for loop inside of another for loop.
• For example, lets make a multiplication table:
• ```
>>> for i in range(1, 5):
...     for j in range(1, 5):
...         print i, '*', j, '=', i * j, '\t',
...     print
...
1 * 1 = 1 	1 * 2 = 2 	1 * 3 = 3 	1 * 4 = 4
2 * 1 = 2 	2 * 2 = 4 	2 * 3 = 6 	2 * 4 = 8
3 * 1 = 3 	3 * 2 = 6 	3 * 3 = 9 	3 * 4 = 12
4 * 1 = 4 	4 * 2 = 8 	4 * 3 = 12 	4 * 4 = 16

```
• Notice the extra comma at the end of the print. This tells print not to output a newline character. The '\t' is a tab character.

# Nested For Loop Exercise

You may not use Python's repetition operator, *, for this exercise.

You must prompt the user in main() for the length of a side.

• Write nested for loops in a function called square(length) that will print a square box with the number of stars given by the length passed in.
```                        * * * * * *
*         *
*         *
*         *
*         *
* * * * * *
```
In this case, length was 6.

• Write nested for loops in a function called triangle(length) that will print an isoceles right triangle like this one which has the number of stars given by the length passed in.
```                        *
* *
*   *
*     *
*       *
* * * * * *
```
In this case, length was 6.

# While Loops

• Sometimes we don't know ahead of time when a loop should end, so we can't use a for loop.
• We can use a while loop, which loops until a condition is met.
• while loops have the form:
• ```while <condition>:
<body>
```
• The conditional part of the while loop is like the conditional of an if statement. If the condition evaluates to True, the code inside the body gets executed. Once the body executes, the condition is evaluated again and the process repeats. The loop ends when the condition evaluates to False.
• For example, suppose we want to find the first power of 2 that is over 1000. To do this, keep trying 2**n until it is greater than 1000:
• ```
>>> n = 0
>>> while 2 ** n < 1000:
...     print '2 **', n, '=', 2 ** n, 'and is < 1000.'
...     n = n + 1
...
2 ** 0 = 1 and is < 1000.
2 ** 1 = 2 and is < 1000.
2 ** 2 = 4 and is < 1000.
2 ** 3 = 8 and is < 1000.
2 ** 4 = 16 and is < 1000.
2 ** 5 = 32 and is < 1000.
2 ** 6 = 64 and is < 1000.
2 ** 7 = 128 and is < 1000.
2 ** 8 = 256 and is < 1000.
2 ** 9 = 512 and is < 1000.
>>> print 2 ** n
1024
>>>

```
• Just like for loops, the 1000 in this program could be replaced by a variable.

# Interactive Loops

• While loops are good for implementing interactive loops.
• Interactive loops can get an unspecified amount of input from a user by asking the user if s/he has more.
• For example, consider the following program that asks a user for numbers then averages them. It uses the accumulator pattern to accumulate two things, the sum and and the number of items.
• ```
print "This program finds the average of numbers you enter\n"

sum = 0.0
count = 0
moreData = "yes"

# Why do we have to check the length > 0 ?
while len(moreData) > 0 and moreData[0]  == "y":
x = input("Enter a number: ")
sum = sum + x
count = count + 1
moreData = raw_input("Do you have more numbers (yes or no)? ")

if count == 0:
print "\n Sorry you didn't want to run this program"
else:
print "\nThe average of the numbers is", sum / count

```
• input and raw_input both take keyboard input from the user, but they return different things. raw_input always returns a string that is exactly what the user typed in. input tries to evaluate the input, as if it were typed into the Python shell.
• Here's some example input/output:
• ```This program finds the average of numbers you enter

Enter a number: 32
Do you have more numbers (yes or no)? yes
Enter a number: 45
Do you have more numbers (yes or no)? y
Enter a number: 34
Do you have more numbers (yes or no)? y
Enter a number: 76
Do you have more numbers (yes or no)? y
Enter a number: 45
Do you have more numbers (yes or no)? nope

The average of the numbers is 46.4
```

# Sentinel Loops

• Sentinel loops are the same as interactive loops, except that they wait for a specific number as input, then stop.
• The sentinel is the value that will cause the loop to stop.
• Sentinel loops have the following general form:
```get the first data item
while item is not the sentinel:
process the item
get the next data item
```
• Notice that we need to get the first data item before we enter the loop. This is known as doing the priming read.
• For example, we can reimplement our interactive loop as a sentinel loop:
• ```
# Filename:    sentinel.py
# Author:      Don Miner & Sue Evans
# Date:        8/9/09
# Section:     All
# Email:       bogar@cs.umbc.edu
# Description: This program illustrates use of a sentinel
#              loop with a priming read.  It also shows
#              the use of a constant.

def main():

print "This program finds the average of numbers you enter\n"

sum = 0.0
count = 0

# QUESTION is in all caps because it's a constant
# Using constants can save a lot of typing
QUESTION = "Enter a number (0 to quit): "

x = input(QUESTION)

# sentinel loop using a sentinel of 0
while x != 0:
sum += x
count += 1
x = input(QUESTION)

if count == 0:
print "\n Sorry you didn't want to run this program"
else:
print "\nThe average of the numbers is", sum / count

main()

```
• Here's some sample input/output:
• ```This program finds the average of numbers you enter

Enter a number (0 to quit): 32
Enter a number (0 to quit): 45
Enter a number (0 to quit): 34
Enter a number (0 to quit): 76
Enter a number (0 to quit): 45
Enter a number (0 to quit): 0

The average of the numbers is 46.4

```
• What happens if the user enters the sentinel at the priming read ?

# Lots of Data

• What if we made a mistake near the end of inputting a lot of data ?
• What if there is no good sentinel value ?
• We could read from a file !
If the numbers are in a file, we don't have to worry about typos, or how many numbers there are, or having a good sentinel.
• Here's a sample data file, data.dat
• ```12
21
35
-5
90
75
59
-23
40
0
57
32
-12
45
61
110
94
37
77
31
99
38
47
76
17
```
• Recall that to open a file, we simply do the following:
• ```    infile = open(<file name>, <mode>)
```
• Then, to read a line, do the following:
• ```    line = infile.readline()
```
• While loops are great for reading files, because many times we don't know how long the files are.
• The following program opens a file, reads it line by line, then takes the average:
• ```
# Filename:    filewhile.py
# Author:      Don Miner & Sue Evans
# Date:        8/9/09
# Section:     All
# Email:       bogar@cs.umbc.edu
# Description: This program illustrates use of a while
#              loop to read from a file and stop at
#              the end-of-file.

def main():

print "This program finds the average of numbers found in a file\n"

fileName = raw_input("What file are the numbers in ? ")
infile = open(fileName, "r")

sum = 0.0
count = 0

# readline() will return "" when the file is done
while line != "":

# float(n) tries to convert whatever n is to a float
sum += float(line)
count += 1

if count == 0:
print "\n Sorry you didn't want to run this program"
else:
print "\nThe average of the numbers is", sum / count

main()

```
• Here's the output using data.dat as the input file : "
• ```
linuxserver1.cs.umbc.edu[123] python filewhile.py
This program finds the average of numbers found in a file

What file are the numbers in ? data.dat

The average of the numbers is 44.52
linuxserver1.cs.umbc.edu[124]

```
• Remember that reading a file is even easier with a for loop:
• ```
# Filename:    filefor.py
# Author:      Don Miner & Sue Evans
# Date:        8/9/09
# Section:     All
# Email:       bogar@cs.umbc.edu
# Description: This program illustrates use of a for
#              loop to read from a file and stop at
#              the end-of-file.

def main():

print "This program finds the average of numbers found in a file\n"

fileName = raw_input("What file are the numbers in ? ")
infile = open(fileName, "r")

sum = 0.0
count = 0

# Python automatically goes line-by-line
for line in infile:

# float(n) tries to convert whatever n is to a float
sum += float(line)
count += 1

if count == 0:
print "\n Sorry you didn't want to run this program"
else:
print "\nThe average of the numbers is", sum / count

main()

```
• Here's the output from this version :
• ```
linuxserver1.cs.umbc.edu[125] python filefor.py
This program finds the average of numbers found in a file

What file are the numbers in ? data.dat

The average of the numbers is 44.52
linuxserver1.cs.umbc.edu[126]

```

# Post-test loop

• In a situation where a user is giving input, sometimes we would like to keep asking the user until s/he gives us valid input.
• To do this, we would use a post-test loop, which iterates until the condition has been satisfied.
• For example, we want to get input from the user until s/he provides us with a positive number :
• ```
number = -1
while number < 0:
number = input("Enter a positive number : ")

```
• Notice that an alternative to using a priming read with a while loop, is to start with an illegal value to get into the loop. This is a common practice with post-test loops
• A good example using a post-test loop is the function getValidInt() shown below :
```
# Filename:    getValidInt.py
# Author:      Sue Evans
# Date:        8/9/09
# Section:     All
# Email:       bogar@cs.umbc.edu
# Description: This program illustrates use of a while
#              loop to get values from the user within
#              a specified range, rejecting all bad input.
#              The function uses a post-test loop.

def getValidInt(question, min, max):

# use a bad value to enter the loop
value = max + 1

# compose the prompt
prompt = question + " (" + str(min) + "-" + str(max) + "): "

# continue to get values until the user enters a valid one
while value == "" or value < min or value > max:
value = raw_input(prompt)
if len(value) != 0:
value = int(value)

# return a valid value
return value

def main():

print "\nThis program will give the classifications"
print "of integers in the range you select.\n"

START_QUES = "Enter the starting integer"
END_QUES = "Enter the ending integer"

start = getValidInt(START_QUES, 1, 10000)
stop  = getValidInt(END_QUES, start, 10000)

print "\nYou selected %d to %d.\n" % (start, stop)

main()
```

Let's watch it work.

```linuxserver1.cs.umbc.edu[121] python getValidInt.py

This program will give the classifications
of integers in the range you select.

Enter the starting integer (1-10000): 10001
Enter the starting integer (1-10000): 0
Enter the starting integer (1-10000):
Enter the starting integer (1-10000): -2
Enter the starting integer (1-10000): 5
Enter the ending integer (5-10000): 4
Enter the ending integer (5-10000): 10001
Enter the ending integer (5-10000): 23

You selected 5 to 23.

linuxserver1.cs.umbc.edu[122]

```

• The following program uses many of the types of loops we've been studying.
• main() uses a sentinel loop.
• The function getValidInt() uses a post-test loop.
• The function drawRectangle() uses nested for loops.
• A common way of interacting with the user is to use a menu. Here's a program that works with rectangles and uses a menu :
```# Filename:    rectangles.py
# Author:      Sue Evans
# Date:        8/12/09
# Section:     all
# Email:       bogar@umbc.edu
#
# This program uses a menu to interact with the user.
# It also makes use of functions.

# findArea() finds the area of a rectangle
# Inputs: length and width
# Output: the area of the rectangle
def findArea(length, width):

return length * width

# findPerimeter() finds the perimeter of a rectangle
# Inputs: length and width
# Output: the perimeter of the rectangle
def findPerimeter(length, width):

return 2 * (length + width)

# drawRectangle() draws the rectangle using *s
# Inputs: length and width
# Output: none
def drawRectangle(length, width):

print
for i in range (0, length):
for j in range (0, width):
# if it's on the border print a star else a space
if i == 0 or j == 0 or i == length - 1 or j == width - 1:
print '*',
else:
print ' ',
print

# enterDimensions() allows the user to enter
# the dimensions of a rectangle.
# Inputs: none
# Output: length and width
def enterDimensions():

length = getValidInt("Enter the length", 1, 10)
width = getValidInt("Enter the width", 1, 10)

return length, width

# getValidInt() prompts the user to enter an
# integer in the specified range, rejects values
# not in that range by requiring new input, and
# only returns a valid integer.
# Inputs: the question of the prompt,
#         the minimum value in the range
#         and the maximum value in the range
# Output: an integer in the specified range
def getValidInt(question, min, max):

# use a bad value to enter the loop
value = max + 1

# compose the prompt
prompt = question + " (" + str(min) + "-" + str(max) + "): "

# continue to get values until the user enters a valid one
while value == "" or value < min or value > max:
value = raw_input(prompt)
if len(value) != 0:
value = int(value)

# return a valid value
return value

# Inputs: none
# Output: none
print "\n\tE - Enter a rectangle's dimensions\n"
print "\tA - Find the area of the rectangle\n"
print "\tP - Find the perimeter of the rectangle\n"
print "\tD - Draw the rectangle\n"
print "\tQ - Quit\n\n"

# printGreeting() prints the greeting to the user
# Inputs: none
# Output: none
def printGreeting():
print "\nThis program works with rectangles."
print "It can find the area and perimeter of a rectangle"
print "whose dimensions you've entered and can even draw it."
print "You can continue to enter as many rectangles as you want.\n"

def main():

choice = ''

printGreeting()
length, width = enterDimensions()

while choice != 'Q' and choice != 'q':
choice = raw_input("Enter your choice : ")

if choice == 'E' or choice == 'e':
length, width = enterDimensions()
elif choice == 'A' or choice == 'a':
area = findArea(length, width)
print "The area of a %d x %d" % (length, width),
print "rectangle is %d\n\n" % area
elif choice == 'P' or choice == 'p':
perimeter = findPerimeter(length, width)
print "The perimeter of a %d x %d" % (length, width),
print "rectangle is %d\n\n" % perimeter
elif choice == 'D' or choice == 'd':
drawRectangle(length, width)
elif choice == 'Q' or choice == 'q':
print
else:
print choice + ' is not a valid choice\n'

main()

```

Let's see it run.

```linuxserver1.cs.umbc.edu[101] python rectangles.py

This program works with rectangles.
It can find the area and perimeter of a rectangle
whose dimensions you've entered and can even draw it.
You can continue to enter as many rectangles as you want.

Enter the length (1-10): 4
Enter the width (1-10): 7

E - Enter a rectangle's dimensions

A - Find the area of the rectangle

P - Find the perimeter of the rectangle

D - Draw the rectangle

Q - Quit

The area of a 4 x 7 rectangle is 28

E - Enter a rectangle's dimensions

A - Find the area of the rectangle

P - Find the perimeter of the rectangle

D - Draw the rectangle

Q - Quit

The perimeter of a 4 x 7 rectangle is 22

E - Enter a rectangle's dimensions

A - Find the area of the rectangle

P - Find the perimeter of the rectangle

D - Draw the rectangle

Q - Quit

* * * * * * *
*           *
*           *
* * * * * * *

E - Enter a rectangle's dimensions

A - Find the area of the rectangle

P - Find the perimeter of the rectangle

D - Draw the rectangle

Q - Quit

Enter the length (1-10): 9
Enter the width (1-10): 4

E - Enter a rectangle's dimensions

A - Find the area of the rectangle

P - Find the perimeter of the rectangle

D - Draw the rectangle

Q - Quit

* * * *
*     *
*     *
*     *
*     *
*     *
*     *
*     *
* * * *

E - Enter a rectangle's dimensions

A - Find the area of the rectangle

P - Find the perimeter of the rectangle

D - Draw the rectangle

Q - Quit

w is not a valid choice

E - Enter a rectangle's dimensions

A - Find the area of the rectangle

P - Find the perimeter of the rectangle

D - Draw the rectangle

Q - Quit

Enter the length (1-10): 12
Enter the length (1-10): 4
Enter the width (1-10): 10

E - Enter a rectangle's dimensions

A - Find the area of the rectangle

P - Find the perimeter of the rectangle

D - Draw the rectangle

Q - Quit

* * * * * * * * * *
*                 *
*                 *
* * * * * * * * * *

E - Enter a rectangle's dimensions

A - Find the area of the rectangle

P - Find the perimeter of the rectangle

D - Draw the rectangle

Q - Quit

linuxserver1.cs.umbc.edu[102]

```

# break

• break can be used to exit the current loop.
• For example, we can rewrite our sentinel average example so that it doesn't need a priming read :
• ```sum = 0.0
count = 0.0

while True:

x = input("Enter a number (0 to quit): ")

# if x is 0 we'll stop this loop now
if x == 0:
break

sum = sum + x
count = count + 1

print "\nThe average of the numbers is", sum / count
```
• What happens if we wrote the if statement wrong and it never evaluates to true?
• break also works with for loops.
• Although break works, it should be avoided because it damages the readability of your code. Programmers expect to see the real continuation condition behind the word while. Using while True with a break somewhere within the loop causes the reader to go through the whole body of the loop to see what the termination condition is.
• Use of break is not allowed in CMSC 201. See the class standards.

# continue

• Sometimes, we may want to skip part of a loop.
• In the case of the for loop, when the continue is encountered, the next value will be placed into the loop control variable and the next iteration will begin. An iteration is one time through the loop.
• For example, lets write a for loop that prints out numbers that are divisible by 3 or 7:
• ```>>> for n in range(30):

...     if not (n % 3 == 0 or n % 7 == 0):
...         continue
...     print n,
...
0 3 6 7 9 12 14 15 18 21 24 27 28
```
• Why doesn't this print out 30?
• In this example, n is the loop control variable.
• continue should be avoided for the same reasons as break: it makes loops harder to read and disrupts normal control flow.
• How can this example be simplified?
• Use of continue is not allowed in CMSC 201. See the class standards.

# Caution!

```# Filename:    beers.py
# Author:      An unknown UMBC student
# Date:        Every semester
# Section:     Can never remember
# Email:       unknown@umbc.edu
#
# How many beers are left after 5 rounds?

def main():

beers = 1.0
CHORUS = 'bottles of beer'

# You'd think this loop will count down from 1.0 to 0.0 by
# step -0.2, singing each verse as it goes, but does it ?
while beers != 0.0:
print "%f %s on the wall" % (beers, CHORUS)
print "%f %s" % (beers, CHORUS)
print "Take a fifth down, pass it around"
beers -= 0.2
print "%e %s on the wall\n" % (beers, CHORUS)

main()

```
```linuxserver1.cs.umbc.edu[101] python beers.py
1.000000 bottles of beer on the wall
1.000000 bottles of beer
Take a fifth down, pass it around
8.000000e-01 bottles of beer on the wall

0.800000 bottles of beer on the wall
0.800000 bottles of beer
Take a fifth down, pass it around
6.000000e-01 bottles of beer on the wall

0.600000 bottles of beer on the wall
0.600000 bottles of beer
Take a fifth down, pass it around
4.000000e-01 bottles of beer on the wall

0.400000 bottles of beer on the wall
0.400000 bottles of beer
Take a fifth down, pass it around
2.000000e-01 bottles of beer on the wall

0.200000 bottles of beer on the wall
0.200000 bottles of beer
Take a fifth down, pass it around
5.551115e-17 bottles of beer on the wall

0.000000 bottles of beer on the wall
0.000000 bottles of beer
Take a fifth down, pass it around
-2.000000e-01 bottles of beer on the wall

-0.200000 bottles of beer on the wall
-0.200000 bottles of beer
Take a fifth down, pass it around
-4.000000e-01 bottles of beer on the wall

-0.400000 bottles of beer on the wall
-0.400000 bottles of beer
Take a fifth down, pass it around
-6.000000e-01 bottles of beer on the wall

-0.600000 bottles of beer on the wall
-0.600000 bottles of beer
Take a fifth down, pass it around
-8.000000e-01 bottles of beer on the wall

```

I had to stop this code from running using ^c
It's an endless loop.

### The Lesson

• Don't test floating point numbers for equality.
• The culprit: 1/5 is not a terminating fractional number in base 2
(similar to 1/3 in base 10). 1/510 = 0.001100110011... 2 repeating.