Getting Loopy

See also

Text, Chapter 5

Loops are a general tool used any time there is some task to do over and over. Your job as a programmer is to find those places in your problem where a loop is needed, then figure out how to set up the loop to do the job.

Kinds of Loops

Python, and most modern programming languages, has several different kinds of loops you can use. Why more than one is just a matter of convenience. You can get away with fewer, in fact only one, but life is easier when we have options.

The basic kinds of loops we will study are these:

While loop

While some condition is true, do something. In Python, this looks like this:

while x > 0:
    # do something here

The something we want to do is any sequence of Python statements indented as shown in the comment above. That sequence is called the loop body. The flow chart diagram for this looks like this:

../_images/while-loop.png

Of all the loops we will study, this is the one I would keep if I had to choose. Another name for this kind of loop is the pre-test loop because we test the condition before we process the loop body. It may be that we never process the loop body, if the condition comes up False the first time we test:

num = 5
while num > 5:
    print(num)

We will not see any output from this sequence since the condition is initially False.

Do Until loop

The next loop is similar, but waits to ask the question until after you do the task. This one is not used as much, and Python does not provide a loop of this kind.

This kind of loop is also called the post-test loop because the condition is evaluated after the loop body is processed. The minimum number of times we will process the loop body is once, since we do that before ever checking the condition.

Counted loop

Both of the above loops run until something happens to stop them. If you think about it, that something must happen inside the loop so the condition we are checking causes the loop to stop.

The counted loop, does something exactly the right number of times. The basic Python counted loop looks like this:

for count in range(1,10):
    print(count)

The only annoying thing about this loop is how that range function works. Formally, it produces a list of numbers starting with the first number and going up to but not including the last number. So we see this:

>>> for count in range(1,10):
...     print(count)
...
1
2
3
4
5
6
7
8
9
>>>

Note

We have not studied the list in Python yet, so for now, just think of it this way. count is a loop variable that is automatically set to a value from the sequence of values produced by range each time the loop is processed. On the first time through the loop, the value is 1, on the second pass, the value is 2 and so on.

Counted loops are used most often when you know exactly how many times you want the loop body to be processed. If that number of times is not known, we probably need to use the while loop above.

Learning when to use loops

The big challenge new programmers face is seeing a loop as part of the solution. Each problem has its own issues, bu the key thing you need to see is that you are doing some set of things over and over. Let’s look at a simple example.

I need to add up a bunch of numbers the user will type in from the console. Is that a place for a loop.

Absolutely! As stated, I do not even know how many numbers I might need to add up, and even if I did know the number, it is still a looping opportunity. To see this more clearly, we need to think about how we would add up the numbers manually.

Summing by hand

If you were given this problem to do by hand, you would probably write down the entire list of numbers on a piece of paper and start adding all of the digits in the ones column, writing down the result and copying the carry into the next column. You know the drill. This does not seem like a loop at all, but it can be recast as one if we think about how we would do it with a calculator!

What you do now is first, clear the accumulator (the number on the display). You enter the first number, then hit the plus key. You see the first number on the display. That number is actually the starting value (0) plus the number you just entered. Hitting the plus key tells the calculator to add in whatever number you type in next. Keep doing the sequence (enter number, then hit the plus key) until all numbers have been entered, then hit the equals key to see the final result.

All those steps were pretty much identical, with a few exceptions, so we are actually doing the same thing multiple times.

We can translate this process into Python code as follows.

First we need to create a variable that will hold the sum. Let’s call it sum for convenience. The initial value for this variable should be zero, since we have not added in anything yet. (This is the same step as clearing the calculator display!).

Now, our loop involves getting a new number and adding it in. Let’s try this:

sum = 0
new_number = int(input("Enter the next number"))

Well, durn. I wanted to use a loop, but I am stuck. How am I going to control the loop? I need to be able to say NO!, I do not want to enter another number.

The solution to this dilemma is to figure out a possible number that you never expect to enter, and ask the user to enter than number to stop the process of adding in new numbers. (If you know how many numbers to add, this will not be necessary, as we shall see in a bit.

Let’s assume that we only want to add up positive numbers (perhaps including zero). We could ask the user to enter a negative number to stop the loop:

sum = 0
new_number = int(input("enter a number (negative to quit)"))
while new_number >= 0:
    sum = sum + new_number

So far we have our first number added in, but running this loop will not be a good idea. We never get another number since the input function was called outside of the loop. We need to get another number. The solution is to get that next number by repeating the input line, but this time inside the loop body. Like this:

sum = 0
new_number = int(input("enter a number (negative to quit)"))
while new_number >= 0:
    sum = sum + new_number
    new_number = int(input("enter a number (negative to quit)"))

We need to see the final sum, so we need one more statement, this one to print out the final value for sum. That one is not in the loop, so we do not indent it. Here is the final code:

sum = 0
new_number = int(input("enter a number (negative to quit)"))
while new_number >= 0:
    sum = sum + new_number
    new_number = int(input("enter a number (negative to quit)"))
print("The sum of the input numbers is", sum)

Here is a sample run:

Enter a number (negative to quit)10
Enter a number (negative to quit)20
Enter a number (negative to quit)30
Enter a number (negative to quit)40
Enter a number (negative to quit)-10
The sum of the input numbers is 100

Looks like it works.

Summing a known number of numbers

If we know how many numbers we want to add up, the code is easier. We will use a for loop to do this one:

for count in range(1,6):
    new_number = int(input("Enter value",count)

This loop just asks for exactly five numbers (why?). It does no work. Let’s add those numbers up by using some of the logic we used before:

sum = 0
for count in range(1,6):
    new_number = int(input("Enter value",count)
    sum = sum + new_number
print("The total of the numbers input is",sum)

See how this one works. We did not need to start off by reading a number, then checking it since we knew exactly how many numbers we would input.

Replacing the for loop with a while loop

We have seen this problem before, time to look at how you should have done it.

The for loop sets up a counter and gives it an initial value. That value is the lower number we set up in the range. The range generates a sequence of numbers as we saw earlier. We want our while loop to process code exactly the same number of times, with a new value in loop each pass. Here is how that might be done:

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

What do you expect to see? The answer should be the same set of numbers we saw earlier.

In this code, we needed to generate the right sequence of values for count ourselves, and we did this by adding one each pass. Make sure you see that this does the job!

Note

I will leave it as an exercise to add in the rest of the summing logic to add up the same set of numbers.

Infinite loops

There is an interesting kind of loop that never stops! Sounds kind of useless, but it turns out to be handy in some cases. Here is what it looks like:

count = 1
while True:
    print(count)
    count += 1

Note

Whoa! What was that last line?

Python lets you be lazy (remember, good programmers are lazy) any time you want to add something to a variable and put it back in the same variable. The += notation is just a short form for count = count + 1. We can also do these:

  • count += 2 # add two each pass

  • count /= 2 # divide the value in half each time

Neat! (I bet you can think up a few more variations)

Back to our infinite loop, running this example will never end. Try it and see what happens.

Eventually, the number might get so bit that Python will pitch a fit, but that will take a while. You can press Ctrl-C on the PC to stop the program. If all else fails, close the console window!

Escaping from this prison

What do you do when you wanr to escape from prison? Break out, of course (NO!, I do not condone such behavior!)

Python has a nice escape statement that will get you out of any loop you are in at the moment. Here is what it looks like:

count = 1
while True:
    print(count)
    if count > 10:
        break
print("I am free!!")

The break statement will only be executed when the value in the count variable gets to a value of 11. The break will exit the infinite loop and we will continue processing on the statement after the loop.

Combining loops

We cah have loops inside other loops with no problems, just indent properly and you are good!

Suppose you want to print out that old multiplication table from elementary school. Here is some code that might do the job:

# generate the multiplication table

for row in range(1,11):
    for col in range(1,11):
        value = row * col
        print(value, end=' ')
    print()

The new stuff at the end of the print function forces Python to add a space after each thing it prints, but not to add a newline. The result is that the numbers have spaces between them, and we do not generate a newline until we want one. I want one after each row is complete, so I add a print statement after displaying a complete row,

Running this code give this:

1 2 3 4 5 6 7 8 9 10
2 4 6 8 10 12 14 16 18 20
3 6 9 12 15 18 21 24 27 30
4 8 12 16 20 24 28 32 36 40
5 10 15 20 25 30 35 40 45 50
6 12 18 24 30 36 42 48 54 60
7 14 21 28 35 42 49 56 63 70
8 16 24 32 40 48 56 64 72 80
9 18 27 36 45 54 63 72 81 90
10 20 30 40 50 60 70 80 90 100

Well, that is close to what I want to see, but it is ugly. Can’t we do something about the way things do not line up? The answer is yes, but we need a trick we have not studied yet:

# generate the multiplication table

for row in range(1,11):
    for col in range(1,11):
        value = row * col
        print("%3d" % value, end=' ')
    print()

See that funny stuff in the print statement. The "%3d" says print something here that will be a decimal number three characters wide. The % val right after that says to use the current value stored in the val variable as the thing to display. Before we added this code, Python printed things any way it chose to, and we got ugly, but readable output.

Here is what we get now:

.
  1   2   3   4   5   6   7   8   9  10
  2   4   6   8  10  12  14  16  18  20
  3   6   9  12  15  18  21  24  27  30
  4   8  12  16  20  24  28  32  36  40
  5  10  15  20  25  30  35  40  45  50
  6  12  18  24  30  36  42  48  54  60
  7  14  21  28  35  42  49  56  63  70
  8  16  24  32  40  48  56  64  72  80
  9  18  27  36  45  54  63  72  81  90
 10  20  30  40  50  60  70  80  90 100

Note

ignore that funny dot at the beginning, I had to put that in to force my note system to display the output correctly.

We will learn more about all this later in the course.

One last try:

# generate the multiplication table

# display a header row at the top
for row in range(11):
    if row == 0:
        print("      ",end='')
    else:
        print("%3d" % row, end=' ')
print()
print("    ","____"*10)

for row in range(1,11):
    for col in range(1,11):
        if col == 1:
            print("%3d" % row," |", end='')
        value = row * col
        print("%3d" % value, end=' ')
    print()

This has logic in it that is trying to display the header row and column to complete the table.

;
        1   2   3   4   5   6   7   8   9  10
     ________________________________________
  1  |  1   2   3   4   5   6   7   8   9  10
  2  |  2   4   6   8  10  12  14  16  18  20
  3  |  3   6   9  12  15  18  21  24  27  30
  4  |  4   8  12  16  20  24  28  32  36  40
  5  |  5  10  15  20  25  30  35  40  45  50
  6  |  6  12  18  24  30  36  42  48  54  60
  7  |  7  14  21  28  35  42  49  56  63  70
  8  |  8  16  24  32  40  48  56  64  72  80
  9  |  9  18  27  36  45  54  63  72  81  90
 10  | 10  20  30  40  50  60  70  80  90 100

Looks more like what we expect, right?