CS 1110 - Introduction to Programming

Starting Out with Python
Chapter 4, Repetition Structures

Objectives:

This lesson covers looping, also called iteration and repetition. Objectives important to this lesson:

  1. The while loop
  2. The for loop
  3. Running totals
  4. Sentinels
  5. Input validation
  6. Nested loops
Concepts:
Chapter 4

As I mentioned last week, there are three structures that are used in most programs. This week we look at the third one, iteration, a formal name your text does not appear to enjoy using.

  • Iteration - Sometimes you want a program to repeat a series of instructions, as long a something is true, or until something is true. Repeating a code section is called iteration or looping, or as this text calls it, repetition.

The text begins on page 159 to convince us that writing the same instruction over and over, such as asking for a each of a hundred salepeople's data, is wasteful and prone to error. There is a better way, especially when we can use variables and loops.

The text tells us one point of view about loops asks us to consider something before we write one: should this loop be condition-controlled or count-controlled?

  • A condition-controlled loop checks a condition before entering the block of code to be repeated. If the condition is true, the block of code is executed. The condition is checked again, and if it is still true, the loop is repeated. It should be obvious to you that in order to exit the loop, something must happen that will eventually cause the condition to be false. The change may happen due to what the block of code does, due to encountering the end of a stream of data, or could even be due to the time of day changing, but it must eventually happen, or we are stuck in an infinite loop, one that will never end.
  • A count-controlled loop is meant to run a specific number of times. Perhaps the user will input a number for a variable that will control the number or repetitions, or perhaps the programmer will set the number based on some requirement for the program. In either case, there is typically a variable whose value is incremented each time the loop runs, and the loop will repeat until that variable's value reaches the specified limit.

The first loop that is discussed is the while loop. It is an example of a condition-controlled loop. The basic structure is shown on page 161:

while condition:
    statement
    statement
    statement
first line after the loop

This structure is like the if structure we saw last week: it begins with a keyword, there is a condition that must be evaluated, and there is a colon after the condition. Any number of code statements may be included in the code block, but all must be indented by the same amount, unless there is another structure inside the block, which would require more indention.

  • If the condition is True, the statements in the loop's code block are executed, then the condition is evaluated again.
  • If the condition is False to begin with, the loop will not run.
  • If the condition becomes False while the loop is running, it will stop running the next time the condition is evaluated.
  • If the loop is exited, or if it is never entered, the program continues with the next line of code after the loop.

In the example on page 163, the user is asked at the bottom of the loop's code block whether the program should run the loop again. If so, the condition is not altered, but if not, the condition is changed so that it is set to False.

The text points out that a while loop is an example of a pre-test loop, also called an entry-condition loop. This means that a while loop may never run if its condition is false on the first evaluation.

On page 167, the text describes an infinite loop (oooh, scary) which has no way to end itself. As a precaution, we are told that we may be able to break out of an infinite loop by pressing ctrl-c. This may not work, so try to never make this coding mistake.

The text move on to its next topic, the for loop. A for loop is a count-controlled loop. The programmer intends for it to run a specific number of times. There are a couple of ways to write a for loop, and the text shows you the simplest way first. (Good for it.)

for variable in [value1, value2, value3]:
    statement
    statement
    statement

In the example structure above, the loop will run three times, because there are three values inside the square brackets. On each pass through the loop, the variable takes on the value of the next value in the set. (Things inside square brackets can be thought of as a set, and that is a much nicer word than the book's choice, which is iterable. Sounds like the loop has a bad attitude.) Assigning to the variable may seem confusing, because there is no assignment operator in sight. It's okay, the for loop knows how to make the assignment. By the way, the text wants us to know that the variable is called the target variable. Why? Well, there could be other variables that are used in the code block, and calling this one the target reminds us that it is the one controlling the loop by receiving the values in the set.

The value of the variable changes each time it runs the loop, until there are no more new values in the set. Note that this method of running a loop does not have a conditional phrase. It does have a variable and a set of values. The implied condition is this: is there a value in the set that has not been used yet? If the answer is yes, the next value is assigned to the target variable and the loop runs. When we run out of values in the set, the loop stops.

That's nice, but it is a bit wordy, in that you have to type out all the values you want to use for the iterations. Pages 170-171 show us a variation on the for loop. It is a bit different. The code could look like this:

for num in range(5)
    statement
next line after the loop

In this example, the name of the variable is num. The range(5) part means to run the loop 5 times. The odd part is that it does not use the values 1 through 5. It uses the values 0 through 4. Why? Because the people who wrote the code for the range function decided that's how they wanted to count. We can remember that the number in parentheses is the cutoff value, or ending limit, and that it is not actually used.

Of course there are other ways to do a for loop. The range function can take more than one argument. Let's try two:

for num in range(1, 5)
    statement

In this case, the 5 means that the ending limit is still 5, but the 1 means that we start counting a 1, not 0. At first glance, you might think this means to use the values 1, 2, 3, 4, and 5, but remember that the ending limit is never used, so the loop would only run for the values 1, 2, 3, and 4. Why would you do that? You would do that if you actually wanted to use the value of the target variable in the code block, and you wanted it to have those values. Breathe for a minute, then we can continue.

So, what if we hand the range function three arguments? Well, the first one is the start value, the second one is still the ending limit, and the third one is the step value. The what? What if I told you to count by twos. Could you do that? (2, 4, 6, 8, and so on...) That is the same as having a start value of 2 and a step value of 2. The step value is the number you add to each value to get the next value. Here is an example from the text:

for num in range(1, 10, 2)
    statements

In this case we start at 1, we add 2 each time, and our ending limit is 10. Wait a minute, that gives us 1, 3, 5, 7, 9, and 11. Well the 11 is greater than the ending value, so it is never used. This loop would use only the odd numbers from 1 through 9. The text points out, along the way, that we can count up with a positive step value, and we can count down with a negative step value. If no step value is assigned, the step value is assumed to be 1.

The text shows us an example of using the value of the target variable on pages 172 and 173. It follows this with the idea of letting the user choose values that control iterations. We have already considered this, so we will move on.

The text continues with a standard idea on page 180. An accumulator is a variable that is used to keep a running total of values entered. This can be useful for a running subtotal of transactions. The sample program on page 180 is simple enough. The key is to initialize the value of the accumulator to 0 before the loop begins. If you initialized it inside the loop, it would only hold the value of the last number entered at the end of the loop.

Page 181 discusses augmented assignment operators, which we have seen before, but this time the discussion is a little clearer.

Operator 
Meaning
+=
x += 10 means the same as x = x + 10
It increments the current value of x by 10
-=
x -= 10 means the same as x = x - 10
It decrements the current value of x by 10.
*=
x *= 10 means the same as x = x * 10
It multiplies the current value of x by 10.
/=
x /= 10 means the same as x = x / 10
It divides the current value of x by 10.
%=
x %= 10 means the same as x = x % 10
It determines the value of x mod 10, and assigns the answer to x.

There is no programmatic advantage to writing the code one way or the other, but you can make your typing job a bit shorter if you have a lot of operations like these to type.

We have seen programs that run a loop a set number of times, and programs that run a loop again if the user says to do so. The text supposes that we may have to write a program that will run a loop an undetermined number of times, and that the user will be resentful if he/she has to request another entry each time. This is likely to be true especially if the data entries are short, and the question/answer sequence effectively doubles the amount of time the user has to operate the program. What do we do? The suggestion in the text is to use a sentinel, also called a sentinel value, which is simply an impossible value that will signal the program to stop running the loop.

The text offers an example of a sentinel program in program 4-13. For clarity, the author has chosen to prompt the user to enter a new data element or the impossible value (0 is used in the example) to end the loop. This would not make our disgruntled user much happier. It might be better to put the sentinel value on the screen every now and again, instead of doing it for every record entered.

I have been warning you about input validation, or which is sometimes error trapping, since the first class. On page 185, the text addresses the subject. Guess what? Loops help us validate data.

We finally get a code example after the operator discussions. In the payroll example, program 4-14, the author has not used any input validation. He demonstrates that the user of that program can enter any number of hours worked, even a value that is impossible. If a person could work every hour in a week, that would be 168 hours (24 * 7, right?). The author feeds the program 400 as the hours worked, and generate a paycheck of $8,000 dollars. We should make sure that does not happen. I will write a code block for it, since the author did not.

# get the number of hours worked
# program limits: no negative number, and no numbers over a double shift, which would be 80 hours
hours = -1.0 # this will initialize hours as a float variable holding -1. we are giving the variable an impossible value so the loop runs at least once
while hours < 0 or hours > 80:
    hours = float(input("Enter the hours worked this week: "))
    if hours < 0 or hours > 80:
         print("That is not a legal value for hours. Let's try again.")

This approach initializes the value of hours to an unacceptable value. It is outside the allowed range of values, so the condition in my while loop determines that the loop must run. We ask the user for hours worked this week, and if the value entered is still unacceptable, an error message is printed on the screen and the loop runs again. If an acceptable value is entered, the program exits the loop and runs the next line of code. Note the colons used after each conditional test. They are not just nice looking, they are required.

In the example code after figure 4-7, the text structures its program a little differently. It asks the user for a value, then runs a loop that checks for a valid/acceptable value. If the value of the entry is not acceptable, an error message appears, and the user is asked for the data again, at the bottom of the loop. That method has two places data entry can occur, mine has one. That method uses one conditional test, mine uses two. Both approaches are valid, and both work. As a programmer, I would choose mine as a method that presents the same input line to the user each time. Fit and feel of the interface can be important to a user. Note, however, that the author's error message is more informative than mine. This leads to a program design choice. Are we supposed to help the user here, or not? In this case it makes sense to help. If we were doing a login event, we should not help very much. Never tell the person logging in whether the error is in the user name or the password. That is too much information to give a hacker.

Section 4.7 of the chapter discusses nested loops, loops that are placed inside other loops. I could have done that with my example above, but I did not need to. One loop over the whole input segment was enough. In the first example in this section, the author is a little confusing. Let's simplify it. He wants a program that will show the current time in hours, minutes, and seconds. If we only wanted to run a loop that showed the hours on a 24 hour clock, it would go from 0 to 23. That is the same as saying we want a loop like this:

for hours in range(24):

In each hour, we need a loop that will show 60 minutes, so we need a loop that does that for each hour. We do that by putting the minutes loop inside the hours loop.

for hours in range(24):
    for minutes in range(60):

It may be obvious that we need a loop for seconds as well, that will have to run from 0 to 59 for each minute. So we put that loop inside the minutes loop, since it runs its complete loop every minute.

for hours in range(24):
    for minutes in range(60):
        for seconds in range(60):

Finally, we want the output of the program's current values, so the author uses a print function that shows the values of each variable every time the seconds loop complete one pass. Here's what it looks like in my IDLE window.

There is one major problem with this program: it does not take one actual second to run each iteration of the seconds loop, so it runs faster than a real clock. That is a deeper issue, and the author ignores for the moment. The point is to show you that there is a reason to sometimes put a loop inside another loop. You do it when you want the inner loop to run all the way through for each iteration of the outer loop. By the way, when I entered this code in IDLE, it understood that I needed appropriate indents every time I pressed the enter key. That's a nice feature. It is also nice, and not nice, that Python does not require us to mark the end of each loop. Nice that there is no special word to have to enter, but not nice in that there is no special word we can look for as the marker for the end of each loop section.

 

Assignments

Assignments for these chapters will be found in Blackboard. We will explore that in class.