Module 0.5 - Introduction to Loops

Objectives

By the end of this module, for simple HelloWorld-like programs, you will be able to:

  • Identify the new syntactic elements with the basic output-only for loop.
  • Demonstrate ability to mentally trace execution of for loops.
  • Produce desired output using for loops and print statements.
  • Distinguish between count-up and count-down loops.
  • Use some nested for loops with independent variables.
  • Use some nested for loops with dependent conditions.
  • Identify and correct syntax errors related to above objectives.
  • Distinguish between syntax errors and debugging.

0.5.0 - Repetition

Consider any case where you would like to repeat an operation. In Python, this means running some code multiple times.

  • We have already learned how to use a function to re-use code.
  • We will now consider cases where we want re-use code some number of times.
  • Loops let us repeat some operation
    • We can repeat until a condition is met
    • We can repeat a fixed number of times

Let’s start with a simple conditional:

x = 5

if x < 7:
  x = x + 1

print(x)
6
  • On Line 3, we check if x is less than 7
  • Since it is, the indented block (just Line 4) executes
  • x + 1 is 5 + 1, which is 6, which is assigned back into x
  • The program moves on from the if statement
  • The final value of x is 6, which is what is printed

If the condition of the if statement is met, the indented block is run once, and then the program moves on to the next line.

The while loop runs the indented block, and then “loops” back and checks the condition again. The indented block is run until the condition is no longer met.

x = 5

while x < 7:
  x = x + 1

print(x)
7

Execution is somewhat similar to the if statement:

  • On Line 3, we check if x is less than 7
  • Since it is, the indented block (just Line 4) executes
  • x + 1 is 5 + 1, which is 6, which is assigned back into x
  • The program loops back and checks if x is less than 7 again
  • 6 is less than 7, so Line 4 executes again, adding one to x. x is now 7.
  • The program loops back and checks if x is less than 7 again
  • Since x is 7, the loop stops: 7 is not less than 7
  • The program moves on to whatever is next
  • The final value of x is 7, which is what is printed

Let’s modify the code slightly and add print statements to illustrate what’s happening:

x = 4

while x < 8:
  x = x + 1
  print(x)

print("Loop is finished.")
5
6
7
8
Loop is finished.
  • Note that the print statement on Line 5 is indented, which means it is “inside” the loop
  • All of the intendted code will run whenever the loop runs
  • The loop’s condition is only checked after the entire indented block is run

Exercise 0.5.1

Exercise 0.5.1

Write the above in simple_loop.py. Change the the while condition on Line 3 so that the output is:

4
5

0.5.1 - Control Flow

To illustrate the utility of a loop, consider this program that performs a repetitive operation:

print("Hello, world")
print("Hello, world")
print("Hello, world")
print("Hello, world")
print("Hello, world")

This program prints out Hello, world five times. We can implement the exact same functionality with a loop.

  • First, we need to write some code to control what the loop does:
    • A while loop runs as long as a condition is met
    • We need to create a condition that will cause the body of the loop to run five times
    • This is done using a variable we create just for the loop
    • It’s very important that every time the loop runs, we change the variable.

Let’s see how it works:

i = 0

while i < 5:
  print("Hello, world")
  i += 1 # don't forget this!
  • The choice of variable name i is arbitrary, but it is common to use single letters for these looping variables, because they have no other significance.
  • i starts at 0, and the while condition is i < 5.
  • Inside the body of the loop, we add 1 to i each time the loop runs
  • We also perform the operation (in this case, the print statement) each time the loop runs
  • Starting i at 0, running until i < 5, and adding 1 to i each time results in the loop running 5 times

Note: The choice of start and end point is somewhat arbitrary. i could start at 1 and run until i < 6, or start at 1000 and run until i < 1005. However, many things in Python start at 0, so you should generally start “counting variables” at 0. You will see why later in the course!


We can add print statements to examine the value of i while the loop is running:

i = 0

while i < 5:
  print("Hello, world")
  print("i is", end = "")
  print(i)
  i += 1

It’s a good idea to use these kinds of print statments when you are writing your programs. After the program produces the desired behavior, you can remove them.

Exercise 0.5.2

Exercise 0.5.2

Write up the original program as repetition.py (without the print statement for the value of i), but change the while condition so that Hello, world prints out 15 times instead of 5.

Exercise 0.5.3

Exercise 0.5.3

Write up the original program as repetition2.py (without the print statement for the value of i), changing the while condition so that: - Hello, world prints out 10 times instead of 5 - The while condition uses <= (less than or equal to) instead of < (less than)

What happens if we forget to change the variable that we are using to control the loop? Let’s have a look.

i = 0

while i < 1:
  print(i)
  • i is compared to 1: since 0 is less than 1, the inside of the loop runs

  • i is 0, so a 0 is printed

  • i doesn’t change, so i is still 0

  • i is compared to 1 again…

  • This loop will run forever.

  • In practice, it will run until we stop it (or your computer terminates the program for having such an infinite loop).

  • You can try this out safely on your computer, but remember to use Control + C (or click the red stop button) to halt the program.

  • You must have something inside of a while loop that changes the condition of the while statement, otherwise the loop will not terminate!

It is also possible to write a while loop that does not run at all:

i = 0

while i > 1:
  print(i)

This is a simple example, but it illustrates the point: i starts out less than 1, and the while condition is i > 1. The inside of the loop never runs!

0.5.2 - Nesting Loops

  • Any code can go inside the body of a loop, including another loop.
  • Because the body of a loop is established by indenting, a loop inside of a loop will indent twice.
  • This is called “nesting.”
i = 0

while i < 5:
  j = 0
  while j < 3:
    print(i)
    print(j)
    j += 1
  i += 1
  • Notice how the second loop is indented an additional level inside of the first loop
  • Lines 4-9 are in the body of the first loop
  • Lines 6-8 are the body of the second loop
  • The print statements illustrate how i and j change over the course of the loop

  • Remember that an indented block is associated with a loop
  • The inner indented block is associated with the inner loop
  • The inner loop will run multiple times
  • Only after the inner loop finishes will the outer indented loop be complete
  • The outer loop will then run again

Exercise 0.5.4

Exercise 0.5.4

Write up the program as nested_loops.py and run it, observing how i and j change over the course of execution.

Change the program to place the j = 0 outside of the outer loop. How does this change the execution of the program?

Let’s look at another example, where the variables we loop over are more meaningful. This program iterates over hours and minutes:

  • There are 60 minutes, from 0 through 59, in an hour
  • Every 60 minutes, the hour advances
  • We’ll start at hour 9 and go through 11
  • Note the use of end = "" with the print function to keep output on one line
hour = 9

while hour < 12:
  minute = 0
  while minute < 60:
    print("Hour: ", end = "")
    print(hour, end = "")
    print("  Minute: ", end = "")
    print(minute)
    minute += 1
  hour += 1

Exercise 0.5.5

Exercise 0.5.5

Write up the program as times.py and run it. Change the program so that instead of going from hour 9 to hour 11, listing every minute, it goes from 7 to 10, and lists minutes in 15 minute increments.

Your output should start like this:

Hour: 7  Minute: 0
Hour: 7  Minute: 15
Hour: 7  Minute: 30
Hour: 7  Minute: 45
Hour: 8  Minute: 0

and it should end at Hour: 10 Minute 45

0.5.3 - The for Loop

  • We have seen how loops can be used to repeat operations in a manner that we specify.
  • Using a while loop requires us to specify a condition to control the loop
  • Often, we create a temporary variable for this control
i = 0

while i < 6:
  print(i)
  i += 1

This is such a common construct in Python that there is a built-in way to do this: the for loop.

for i in range(6):
  print(i)

Let’s now examine elements of the loop:

There’s the special word for:

for

Then, there’s the for loop variable i:

  • (Just like the while loop, we can name this variable whatever we like)

i

The special word in:

in

The range element that controls the spread of different values that variable i takes on at each iteration of the loop: in:

range

The colon at the end:

colon

Finally, the indented block of code (in this case, just one line) that’s called the body of the loop:

body

How to use range

The range() function in Python actually returns a sequence, or collection, of integers. It can be used in many ways:

With one argument:

  • By default, if range is called with one argument, the collection starts at 0
  • By default, if range is called with one argument, the collection increments by 1 as long as the value is less than the argument
  • So range(6) gives the collection 0, 1, 2, 3, 4, 5 - there are six elements
  • Similarly, range(4) gives the collection 0, 1, 2, 3 - there are four elements

With two arguments:

  • Adding a second argument sets the start and the stop point.
  • The first argument is the start point. This defaults to 0.
  • The second argument is the stop point: the range increments by 1 as long as the value is less than the secnd argument
  • range(0, 6) is the same as range(6): 0, 1, 2, 3, 4, 5
  • range(1, 4) starts at 1 instead of 0: 1, 2, 3

With three arguments:

  • A third arugment tells the range how much to increment
  • The first argument is the start, the second is the stop, the third is the increment
  • range(0, 6, 1) is the same as range(0, 6) or range(6): 0, 1, 2, 3, 4, 5
  • range(0, 6, 2) gives us 0, 2, 4
    • Remember, the range continues as long as the value is less than (but not equal to) the stop value
  • A range can increment in the negative direction: range(5, 2, -1) gives 5, 4, 3
    • The range starts at 5
    • The range stops before being equal to 2
    • The range increments by -1
  • Similarly, range(7, 2, -2) gives us 7, 5, 3

Exercise 0.5.6

Exercise 0.5.6
j = 0

while j < 7:
  print(j)
  j += 1

In simple_for_loop.py, rewrite this program using a for loop.

Exercise 0.5.7

Exercise 0.5.7

In simple_for_loop2.py, write a program that prints out every even number starting with 0 and ending with (including) 84.

Exercise 0.5.8

Exercise 0.5.8

In simple_for_loop3.py, write a program that prints out every odd number starting with 3 and ending with (including) 101.

0.5.4 - Variations

Just like the while loop, the for loop’s looping variable is only necessary to control the flow of execution. We do not need to access it.

Consider this program that repetitively prints out “Hello world” with a while loop:

j = 0

while j < 10:
  print("Hello world")
  j += 1

A similar program with a for loop:

for j in range(10):
  print("Hello world")
  • The for loop is often faster to write than a while loop.
  • You can’t forget to increment the looping variable (and end up with an infinite loop) when using a for loop.
  • Anything that can be done with a for loop can be done with a while loop
  • The reverse is not always true!
  • for loops are powerful and easy to write, so we will use a lot of them

0.5.4 - Loops and Conditionals

Just as mulitple loops can be nested, loops and conditionals can be used inside of each other:

x = 3

if x > 0:
  for j in range(x):
    print(j)
else:
  print("x is too small")

Let’s look at what’s happening here:

  • If x is greater than 0, the if statement’s inside block executes
  • Inside of the if statement is a for loop!
  • The range argument takes x as its argument:
    • The loop iteration starts at 0 and finishes when j becomes greater than x
  • print(j) is indented to be inside of the for loop: it runs once for each value of j
  • If x is not greater than 0, the if statement will not run, but the else will run
  • Inside of the else is a print statement

Exercise 0.5.9

Exercise 0.5.9

Write up the above program as conditional_loop.py. Change x to some number less than 0 and verify what happens.

0.5.5 - Tracing Execution

We’ll now look at an example of how to execute a program “by hand.”” That is, how to trace a program’s execution by methodically following its execution step-by-step. At first this will appear tedious, but it is critical to a firm understanding of how programs execute, and eventually to writing your own programs.

For our example, let’s look at a simple nested loop.. Line numbers can help keep track of where we are in execution of the code.

print(1)                   

for j in range(2, 5):      
    for i in range(j):     
        print(j, end='')
    print()

Let’s now dive into the longer version, just for the sake of understanding.

To make best use of this, open this same page in another browser, and have the program side-by-side, as you read what’s below.

Ready? Let’s trace through:

tracing

  • Right at the start, the first line is print(1). This prints out 1, and moves to the next line.

tracing

  • Initially j = 2 at the start of the outer for loop:

tracing

  • Since j is 2, it’s within the range, and we enter the outer for loop.
    • Now we encounter the inner for loop, where i is set to 0
    • Note: when range has only one number specified, it’s understood to be the upper limit. The upper limit is the current value of j, which is 2.

tracing

  • Inside the inner loop, we execute print(1) ,which, because j is now 2, will print 2.

tracing

  • We’re at the end of the inner loop, so now i increments to 1:

tracing

  • So now inside the inner loop, we print 2 again.

tracing

The output currently looks like:

1
22
  • Then, at the end of the inner for loop, we return to the top where i increments to 2. Since i is at the limit, we exit the inner for loop.

  • Notice: the inner loop iterated twice. Next, we go past the inner loop to print() , which goes to the beginning of the next line.

tracing

tracing

The print statement here completes the first iteration of the outer loop, after which we go to the top of the outer loop and increment j.

tracing

Execution now enters the outer loop with j set to 3.

  • Now we encounter the inner for loop, where i is set to 0 The upper limit is the current value of j , which is 3.

tracing

  • So, the inner loop executes three times, with i first set to 0, then to 1, then to 2.
  • This will result in printing three 3’s. When i becomes 3, it hits the inner loop’s limit and proceeds to the print() that follows. The output so far is:
1
22
333

This completes the iterations of the inner loop with the outer loop j set to 3. Next, j becomes 4

tracing

  • The inner loop starts with i set to 0.
  • Each time through i increments.
  • Until i hits the limit j (which is 4 now).
  • This results in four 4’s being printed in a line. Then we come out of the inner loop and execute print() , which goes to the next line. The output so far is
1
22
333
4444

Finally at the end of the outer loop, j becomes 5 and hits the limit of the outer loop

tracing

Yes, that was long. But doing this many times will help you understand how to read programs. Later, you will become good at this and will, with a quick glance at the inner loop, say “This prints 2 twice in the first iteration of the outer loop.”

Consider this program:

for i in range(1, 6):
    for j in range(i, 0, -1):
        print('*', end='')
    print(' ', end='')
    for j in range(1, i):
        print('-', end='')
    print()
  • First, trace through the values of i and j by hand and try to figure out what gets printed.
  • Do this methodically for each possible value of i and j, remembering that i is now the name of the outer loop variable.
  • Then, edit and execute the above to see if you were right.

Exercise 0.5.10

Exercise 0.5.10

Write a program to print out consecutive integers in a diagonal, as in:

1
 2
  3
   4
    5

Use a nested loops to print the requisite number of spaces before printing each digit. Write your code in diagonal_print.py.

0.5.6 - Reading and Writing

Let’s consider how to read a single for loop, such as:

for k in range(1, 10, 2):
    print(k, end=' ')
    print(2*k)
    print('-')

Exercise 0.5.11

Exercise 0.5.11

Write up the above in simpleloop.py and run to see the output. If the output wasn’t what you expected, trace the execution of the program to see what happened.

Instead of explaining the execution, let’s focus on how to read such a program: The first thing to do is to observe two parts to the loop:

tracing

  • Study the for statement to understand the nature of of the iteration.

tracing

  • Look inside the body.

tracing

Let’s also point out what to keep in mind when writing:

  • First, the for loop header or for statement:

tracing

  • Second, the loop body:

tracing

Good writing habits will save you a lot of trouble. Consistent syntax makes your code readable. Thinking through each part of the program’s execution before writing it will end up being faster than typing before thinking.

Next, let’s combine reading with mental execution. Consider the following program:

def functionOne():
    print('*', end='')

def functionTwo():
    print('*')

def functionThree():
    for j in range(0, 5):
        functionOne()
    functionTwo()

for i in range(1, 11, 2):
    functionThree()

Exercise 0.5.12

Exercise 0.5.12

What does it print? Try to figure this out by mental execution first. Then, type it up and execute to confirm, writing your code in execution_exercise.py.

0.5.7 - Math With Loops

Loops are very useful for mathematical operations, especially ones that rely on sequences:

Consider a program that adds a sequence of numbers:

s = 0

for j in range(10):
  s += j

print(s)
  • Notice how we initialize the variable s outside of the loop: s exists to record the running total.
  • j changes with each iteration of the loop
  • j the value of j is added in to s

What value of s do you expect when the program is finished? Try to trace it before running to confirm.

Exercise 0.5.13

Exercise 0.5.13

In accumulator.py, modify the above program to add all even numbers between 2 and 100.

Exercise 0.5.14

Exercise 0.5.14

In accumulator2.py, write a program that adds up consecutive integers (1, 2, 3, 4… etc.) until the sum is greater than 115, and have the program print out the last number added.

0.5.8 - When Things Go Wrong

As code gets more complex, it gets easier to make mistakes, and harder to find them. In each of the programs below, try to determine the error without compiling the program. Then, write up the program, run it, and see what the interpreter says. After that, fix the error.

Exercise 0.5.15

Exercise 0.5.15
for i in range(0 6):
    print(i)

Write your corrected code in loop_exercise1.py.

Exercise 0.5.16

Exercise 0.5.16
for i in range(0, 6)
    print(i)

Write your corrected code in loop_exercise2.py.

Exercise 0.5.17

Exercise 0.5.17
while i < 10:
    print(i)
    i += 2

Write your corrected code in loop_exercise3.py. (There is more than one “correct” answer.)

Exercise 0.5.18

Exercise 0.5.18
for i in range(0, 6):
print(i)

Write your corrected code in loop_exercise4.py.

Exercise 0.5.19

Exercise 0.5.19
for in range(0, 6):
    print(k)

Write your corrected code in loop_exercise5.py.

Exercise 0.5.19

Exercise 0.5.19
for i in range(0, 6):
    print i

Write your corrected code in loop_exercise6.py.

Let’s point out the difference between a syntax error and a logical error: A syntax error will not allow a program to run. This means you are using the language incorrectly. On the other hand, you could have a program that has no syntax errors (it runs) but it does not produce the desired output. This means there’s a logical error. The process of identifying and fixing errors is called debugging. A bug is an error in software.

Apocryphally, the use of “bug” to describe a software error dates to an incident where Admiral Grace Hopper discovered a moth causing an electrical short in an early (1940s-era) computer. While that incident did indeed happen, use of “bug” to describe an error in an engineering design predates the electronic computer.

Exercise 0.5.20

Exercise 0.5.20

The following code intends to print

55555
4444
333
22
1
for i in range(5, 0, 1):
    for j in range(1, i):
        print(i, end='')
    print()

But there are two bugs. First, try to find the problems solely by reading and mental execution. Then, type up the program in loop_exercise7.py. What does it print? Fix the program to get the desired output.

0.5.9 - Reserved Words

Let’s introduce the notion of reserved words: Some words like for and in are special to the programming language and are called reserved words or key words of the language.

keywords 'for' and 'in'

There are special rules associated with the usage of such reserved words, which we’ll describe over time. For now, we’re just pointing them out. Another example of a reserved word we’ve seen: def It turns out that, even though print looks like it should be a reserved word, it’s in fact not.

  • At this stage, the distinction is not clear and that’s fine.

  • As you learn the language, you’ll learn how to distinguish.

Counting from 0: When a single number like 6 is specified in the range as in range(6),

simple range

Thus, i is 0 the first time, then 1, then 2, and so on until i = 5 (last time through). The number 6 specifies that i cannot be 6 or higher.

There is a certain strangeness to get used to: In programming, the convention is to start counting from 0. This is why units, modules, and assignments start with 0.

  • The other strangeness is specifying the limit as one higher (6) than than last iteration value (5).

The different uses of range : When a single number is specified, as in range(6), the implied start is 0. When two numbers are specified as in: range(10, 16), the first is the start of the count, and the second is the limit:

advanced range

  • Thus i is 10 the first time (this is the first thing that’s printed).
  • Then i becomes 11.
  • Then 12, and so on until the last time through when i is 15. When three numbers are specified as in range(10, 16, 2), the third number is the increment.

advanced range

  • Here, i starts as 10 in the first iteration.
  • Then, i is 12 in the second iteration.
  • Then i becomes 14. This is the last iteration because after that i would be 16, which ends the loop. Using range(10, 15, 2) produces the same result as above, and in fact preferable.

About comments:

First, comments are for human beings to read. The computer does not read them. Thus, in terms of execution, this program

# list the even numbers between 10 and 14, inclusive:
for i in range(10, 15, 2):    # Notice the increment
    print(i)

will result in the exact output as this one:

for i in range(10, 15, 2):
    print(i)

We will use comments in two ways, as in the above example:

  • The first comment is an example of using a comment as a prelude to code, to explain what’s coming or intended.
  • The second kind that is to the right side of a line of code is like an alert: we’ll use this to point out something to pay attention to in that line of code.

End-Of-Module Problems

Full credit is 100 pts (complete at least three problems). There is no extra credit.

Problem 0.5.1 (40 pts)

Problem 0.5.1 (40 pts)

Write a program that uses a loop to print out the the squared value of every integer from -100 to 100 (inclusive), with each value on a new line.

The output should start:

10000
9801
9604

and end

9604
9801
10000

Submit as loop_squares.py.

Problem 0.5.2 (40 pts)

Problem 0.5.2 (40 pts)

The triangle function in this program prints out a triangle of size x to the console:

def triangle(x):
    for j in range(1, x+1):
        for i in range(j):
            print("*", end="")
        print()

triangle(5)

The current output is:

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

Modify the program so that it prints the triangle out upside down:

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

Ensure it works for integer sizes of triangle other than 5. Submit as triangle_printer.py.

Problem 0.5.3 (40 pts)

Problem 0.5.3 (40 pts)

Write a loop that prints out every number from 2 to 1000 (inclusive), each number on a new line, excluding numbers evenly divisible by 6.

Your output will start:

2
3
4
5
7
8

and end:

994
995
997
998
999
1000

Submit this as six_omitter.py.

Problem 0.5.4 (40 pts)

Problem 0.5.4 (40 pts)

Write a program that adds up powers of \(\frac{1}{2}\). That is, approximate the infinite sum:

\(\frac{1}{2} + \frac{1}{4} + \frac{1}{8} + \frac{1}{16} + ...\)

Print out the result after each iteration. Your output should start:

0.5
0.75
0.875
0.9375
0.96875

and it will converge to 1.0.

Loop for at least 100 iterations, but not more than 1000 iterations. Submit as series_summation.py.