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.
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?
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:
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
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]:
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)
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)
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)
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.
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
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):
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):
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.