Lesson 6.1 Introduction to Loops
Lesson 5 showed you how to help your objects make decisions. You even taught your objects a little philosophy, with the introduction of the boolean [true/false] primitive type.
In this lesson, you'll teach your objects to work without constant supervision. Once you teach them the skill of iteration, you can turn them loose with a set of instructions, and not come back until the job is done.
What is iteration? Iteration is a Computer Science term that simply means repeating a set of actions. Iteration is also called repetition or looping. The statements that are used in iteration are called loops.
Iteration and Selection
Both iteration statements and selection statements are flow-of-control statements; they control which code is executed in your program. Like the if statement, iteration is based upon evaluating a boolean--true/false--condition, and then performing a set of actions if the test is true, and skipping them if the test is false. Thus a loop, like an if statement, has both a condition part--the test that is performed--and a body part--the actions that are taken.
Iteration, however, is not the same as selection. Beginning programmers often attempt to use a selection statement where they should use a loop, and vice versa. It is important to be able to distinguish the two, and equally important to be able to select the right control structure for the job.
The selection structure works much like the illustration shown here. [click the image to enlarge]. Driving down a divided highway, you come to a rest stop, and you pull off. When you leave the rest stop, you rejoin the highway a little further down the road.
Once you rejoin the highway, you have no opportunity to go back and revisit the rest stop again. Those who bypass the turnoff, skip the rest stop altogether.
A loop structure looks similar, but not the same. As you can see in this illustration, there is still a boolean test, as there is with selection. But, after you've had your break, the rest stop exit road "loops back" [hence the name], and you rejoin the highway right where you initially left it.
If you like, you can choose to enter the rest stop once again, even though the highway is one-way. In this sense, a loop also works a little bit like a cloverleaf interchange.
Java and Iteration
Java has three loop statements, the while loop, the do-while loop, and the for loop. As you might expect, each of these loops varies in the way that it is used, and the place where it is most effectively employed.
Where?
One difference between the loops is where the test takes place--that is, the relative position of the loop test and the actions contained in the loop body.
One loop, the do-while loop, doesn't make its test until after it has performed the actions in the loop body at least once.
This is called a "test at the bottom" loop. It is also sometimes called a hasty loop or an unguarded loop because it "leaps before it looks".
| {
// Loop body
} |
| Loop Test |
The other two loops, the for loop and the while loop, both make their boolean test before performing the actions in the loop body.
With these loops, if the test condition is false, then the actions inside the loop body are never performed at all. Thus, they are both guarded loops.
| Loop Test |
| {
// Loop body
} |
Counted and Indefinite
Classifying loops according to where their condition is tested helps keep the Java syntax straight in our minds, but it's not really as useful as classifying loops by the kind of bounds that they employ.
A loop's bounds are the conditions under which it will repeat its actions. In a simple, counter controlled loop, the bounds might be expressed as "the counter has a value less than ten". In other, more complex loops, the bounds may be a combination of conditions involving ANDs and ORs.
There are two major kinds of loops that can be built using the basic building blocks [for, while, and do-while] provided by Java. Let's look at them briefly, before we start looking at code in the next section.
Counting or Counted Loops
A counting, [or counted], loop is a loop that repeats a fixed number of times--a "gimme fifty pushups" kind of loop.
With a "pure" counting loop you can look at the code and tell how many times it should execute. In real life, however, things are not so clear-cut. You often won't know what the actual number of repetitions is until your program runs. The number of repetitions may be based upon the number of characters in a String, for instance, or some other number which is not computed until your program runs.
Indefinite Loops
With an indefinite loop, you can never tell how many times the loop will repeat by examining the code. An indefinite loop is a loop that tests for the occurence of a particular event, not a count of the number of repetitions.
"Read characters until you encounter a period" is an example of an indefinite loop. The condition may occur after reading three characters, or it may occur after reading three-thousand. It's also possible that the period might be the first character read or, sometimes, that a period might not occur at all.
There are three commonly encountered types of indefinite loops that you should be aware of. Each of these indefinite loops uses a different sort of bounds:
- Data loop. A data loop is a loop that keeps going until there is no more data. This is used when reading files or sending Web pages over the Net. We'll cover data loops in more detail in Lesson 14, "Streams and Files."
- Sentinel loop. A sentinel loop is an indefinite loop that looks for the presence of a special marker contained within its input. In the "read characters until you encounter a period" example, the period is the sentinel; this kind of loop is a sentinelloop. Sentinel loops are often used in searching, but have other uses as well.
- Limit loop. Limit loops are loops that end when another repetition of the loop won't get you any closer to your goal. Limit loops are often used in scientific calculations and other numeric algorithms, when stating a precise termination condition is not possible. Often this involves monitoring the difference between two variables, and stopping the loop when the difference passes a predetermined threshhold.
A Loop Building Strategy
Writing perfect code the first time is something of a "Holy Grail" among programmers. By that, I mean that most programmers long to do it, but the vast majority consider its attainment to be the stuff of legend.
Writing loops is one area where programming errors often crop up. Several years ago, however, I happened upon a technique developed by Doug Cooper, the Berkeley professor of "Oh! Pascal" fame, for building loops. This technique really does increase your chances of building correct loops the first time, and it's worth your time to learn it.
The Three Faces
If you put a loop inside a program, as we've done with this top-tested loop, you can see that there are:
- actions that occur before the loop is encountered.
- actions that occur inside the loop body.
- actions that occur after the loop is complete.
| // Before |
| Loop Condition |
{
// Inside
} |
| // After |
These three "faces" of a loop are called its precondition, its action or operation, and its postcondition. The loop condition, as you've already learned, is called the loop's bound.
Bound and Goal
The first step in building a successful loop is to be able to describe [and separate] the loop's bound from the loop's goal.
Here's an example:
- Count the characters in a String until a period is encountered. Every String will contain at least one period.
- The goal of this loop is to count the characters that precede a period [that is, a sentence].
- The bounds of the loop are "a period was encountered."
We can use the same bounds with a different goal:
Print each character in a String until a period is encountered.
Here, the bounds is the same, but the goal is different. We're not counting, we're printing.
Step 1: Determine the Bounds
If you've ever bought a new appliance or a new software package, what's the first thing you need to learn? How to stop it once it gets going.
That's where you should always start writing your loops; ask yourself:
"What will make this loop quit?"
"How do I get out of here?"
Making sure your loops can quit is the single most important thing that you can do. Let's take our first example in the previous section, and write the loop bounds in psuedocode:
|
Step 1: The Bounds
|
| // BEFORE
WHILE C IS NOT A PERIOD
{
// INSIDE
}
// AFTER |
Step 2: The Preconditions Step 1, writing the bounds, makes sure that it is possible to get out of the loop. When you write this portion, you'll have to make sure your boolean condition is not unavoidable or impossible, just as you did with selection statements.
Now that you've convinced yourself that it's possible to exit the loop, you have to write the statements that make it possible to enter the loop. Step 1 asks "How do I get out?", while Step 2 asks "How do I get in?"
Precondition statements usually involve creating variables and initializing them to some reasonable state. In our example, we have two variables C, which is a character, and S, which is a String.
Before you encounter the loop bounds, you must make sure that C holds one of the characters in S, otherwise, you have no assurance that you will ever enter the loop--the value in C will be unknown.
In pseudocode, we could write this as:
|
Step 2: The Bounds Precondition
|
S IS A STRING PASSED AS AN ARGUMENT
C IS A CHARACTER
ASSIGN FIRST CHARACTER IN S TO C
WHILE C IS NOT A PERIOD
{
// INSIDE
}
// AFTER |
Step 3: Advance the Loop Now that you've written the bounds and preconditions, it's time to advance the loop. Advancing the loop means that you add statements to the loop body that move you closer toward the loop bounds.
Suppose, for instance that we didn't put any statements inside the loop body used here. What would happen?
- If the String S began with a period, the loop would not be entered and the program would perform correctly.
- Otherwise, when the loop body was entered, nothing in the body would change the value of C, so you would have no way out of the loop; it would simply repeat over and over, endlessly. Such endless loops are very, very common.
To avoid an endless or "dead" loop, you must make sure that the statements on the inside of the loop body change something that is tested in the loop bounds.
In this case, that's as simple as storing the next character in the String S in the variable C, like this:
|
Step 3: Advancing the Loop
|
S IS A STRING PASSED AS AN ARGUMENT
C IS A CHARACTER
ASSIGN FIRST CHARACTER IN S TO C
WHILE C IS NOT A PERIOD
{
ASSIGN NEXT CHARACTER IN S TO C
}
// AFTER |
Step 4: The Goal At this point, the mechanical portion of your loop--the part that makes it "work", so to speak--is finished. You should be able to compile your code [provided you wrote it in a programming language, of course], and it should run correctly.
The whole purpose of a loop, however, is to get some real work done, to accomplish the goal. Up until now, we've ignored the goal portion entirely, because we want to make sure the loop works, before we put it to work.
When writing the code to carry out the goal of the loop, you start at the precondition, move to the loop body, and then deal with the postcondition. You don't have to worry about the postcondition while programming the mechanical operation of the loop, but it is often significant when it comes to the loop's goal.
Here's what to do:
- The precondition involves creating and initializing the variables necessary to carry out the goal of the loop. In our case, the goal is to count the characters in the first sentence, so we need a counter variable initialized to zero.
- The statements inside the loop body usually update the variables created in the precondition. For this example, we merely increment our counter every time through the loop.
- In the postcondition, you often must check one of the bounds conditions to see how to proceed. In our case, that's not necessary, because, if we never enter the loop, our counter will be zero. If the goal of our loop was to average a set of numbers, however, we would first need to check to make sure that the loop had been entered before calculating and printing an average.
Here's the finished pseudocode for our loop:
|
Step 4: Carry Out the Goal
|
S IS A STRING PASSED AS AN ARGUMENT
C IS A CHARACTER
ASSIGN FIRST CHARACTER IN S TO C
COUNTER IS AN INTEGER, INITIAL VALUE ZERO
WHILE C IS NOT A PERIOD
{
ADD ONE TO COUNTER
ASSIGN NEXT CHARACTER IN S TO C
}
COUNTER HOLDS NUMBER OF CHARACTERS IN SENTENCE |
Something to Talk About
Do you understand bounds and goal? Let's see. Use the examples shown above as a guide to write the pseduocode for a loop that averages positive numbers input from the keyboard. Exit the loop when a negative number is entered. What does your code look like? Please continue to the next section of this lesson.
|