| |||||||
Going Around In Circles | |||||||
Conditional statements are a very handy thing, we couldn't write many useful
programs without them. But they are only of limited value. Even with
For example, suppose we wanted to write a program that would get 10 grades from the user and compute the average. One possible top-down-design for this is:
But this is not a great solution. It's going to be a pain to type in as it is, and if we wanted to modify it to compute the average of 100 grades it would be really horrendous. A better design would be:
| |||||||
| The Old Way: Goto |
The question is, how do we "go back to the step two lines up"? In the
old days this was accomplished with something called a
goto statement. You would put a special marker called a label at the
beginning of the part of your program you wanted to repeat. Then at the
point that you wanted to go back to the begining of that part you would
put a goto statement saying which label (since there could be many of
them in a program) you wanted to jump to. The problem is that goto's got used for alot more than just loops, and sooner or later you'd have goto's all over the program pointing every which way, which could make a program just about impossible to understand. This problem of unmanageably tangled programs became known as spaghetti code, and lead to a seminal 1968 paper by Edsgar Dijkstra (Dike-strah) called Goto Statement Considered Harmful.
| ||||||
| The Modern Way: Loop Constructs |
Dijkstra's paper is credited with jump-starting the transition to
what are now called structured languages, such as
Pascal, C, Ada, Java, and modern dialects of Fortran.
The solution used by structured languages is to de-emphasize the use of goto
(though many languages, including C++, still have restricted versions of
it) in favor of specific loop constructs. In Java there are, in fact, 3 different loop constructs available. Which you use depends on the circumstances. The rest of this week's notes will focus on one of these constructs. The remaining two are covered next week.
| ||||||
Counter Loops With For | |||||||
|
The most common situation in which you need a loop is one in which you
want to do something a predetermined number of times, as in the example
above where you want to repeat the process of getting a grade and adding
it to the sum 10 times. The construct used for this is the
for loop, which has the following syntax:
The three expressions in the parentheses together specify the control information for the loop. The first part initializes the variable you will use to control the number of times you want the loop to execute. This variable is called the index variable of the loop. The second part is a test to perform each time you begin to go through the body of the loop. If this test is true, then you proceed into the loop. If it fails, you skip over the body of the loop and continue on with the rest of the program. The last part is something to do each time after going through the body of the loop. After it has been done, control returns to the test at the top of the loop. So, if you wanted to print the numbers from 1 to 5, you could use the following code:
It starts by declaring the The most common counter controlled loop pattern is one like this last one in which you count from some value to another by ones. But there are many other possibilities. For example, we could count from -50 to -100 by twos:
Note that since this loop counts down, we have reversed the sense of the
continuation test from the last example. We use
| |||||||
| The Loop Bounds Need Not Be Fixed |
Of course, when we say that the number of passes through the loop is fixed, we do not
mean that it must be known at compile time. We could for instance
have the loop bounds determined at runtime by some value typed by the user:
| ||||||
| The Scope Of The Loop Control Variable |
In all these examples, we have declared the variable whose value controls
the loop as part of the initialization step in the loop-control header.
While this is not necessary (the variable could be declared at the beginning
of the method with the other variables), it is good style. When the variable is declared in the loop control header its scope is restricted to the loop itself. It can only be referred to in the other parts of the header, and in the statements in the body of the loop.
As with other variables, you should pick the most descriptive name possible.
What is your loop counting? In this case, not much, so we picked the
non-descript name
| ||||||
| When There Is Only One Statement |
As with if the brackets are optional if the body of the
loop is a single statement, like in these examples. But, as with if we recommend
using the braces in any case.
| ||||||
| Averaging 10 Grades With A For Loop |
To return to our original example of averaging ten grades, we arrive at the following design:
This can be translated directly into the following program:
The nice thing about this program is that, if we now want to modify the program to
compute the average of one hundred grades, the only thing we need to do is
change the test from
| ||||||
| Using The Increment Operator |
Notice the use of the pattern
in the loop control header. As we mentioned in the notes two weeks ago, this pattern is very common, and can be abbreviated This allows us to rewrite the header of the for loop in the last program as: This is the most commonly used style of loop header.
| ||||||
| Top-Down Design With For Loops |
As with conditional statements, any arbitrary series of Java statements
can go in the block making up the body of the for loop.
For example, we could take the simple if example
from the last lecture
which asked the user for a number and told them whether it was
less than ten or not, and run it twenty times by putting it inside
a loop from 1 to 20, as in:
We have taken the entire body of the existing program (including the
variable declaration) and just made it into the body of our loop.
As with the loop-control variable declared in the loop header, the
variable The point is that whenever you have a process that calls for doing a given task some number of times, you should be able to break down the design process as requiring you to figure out how to do the task once and then wrapping the result in a loop with the appropriate control. The design of the inner task should not be much affected by the fact that it is part of the enclosing loop. This ability to separate different aspects of the design is what makes top-down design and structured languages such a good fit.
| ||||||
Nested For Loops | |||||||
Just as you can nest an if statement inside a for loop,
you can just as well nest a for loop inside a for loop.
This sort of structure will naturally result from a variety of designs.For example, suppose that you want to print out a multiplication table. How do you do it. Well, let's look at the top down design (We'll assume that the table has the same number of rows and columns.):
So, the outer structure of the program will be a
Now, how do we print a row of the table?
But this is just: Combining these together we get:
We have added a call to
| |||||||
| Positioning Variable Declarations and Initializations | It is important to always be clear on the scope of your variables. That is, the section of within which you can refer to a given variable. Recall that a variable declared in the header of a loop, or in the body of the loop is only available within the code for the loop. In deciding the best place to declare a variable you must take into account where you will need to refer to it. If it is only used to store temporary values during the loop (such as the loop index) then it is good style to declare it in the loop header or body. On the other hand, if it is being used to store some value that will be needed after the loop is completed (such as a sum or product that the loop is being used to compute) then it should be declared outside (before) the loop. A similar issue arises as to where to put the initialization of a variable used in a loop. Typically, the initialization should be before the loop, or, in the case of the loop index, in the loop header. If you put the initialization of a variable insode the loop then the variable will be reset to that value at the start of each cycle through the loop, throwing away any changes that were made to it in the last cycle. This is rarely what you want. In general, it will be apparent from the top-down-design how to position your declarations and initializations. This is just one of many reasons to start with a proper top-down-design. Consider the problem of computing the final grades for a number of students based on their homework grades. This is an extension of the grade averager from above. The high-level design looks like:
This leads to the shell of the program that looks like this:
Now, the action to be taken for each student corresponds pretty much to the design of our previous program to average 10 grades. The only difference is that instead of their being 10 grades, the number of grades is determined by the value that was entered by the user for the number of homeworks. We can simply insert a slightly modified version of the top-down-design for that problem in place in this design:
Thus it is clear that the variables for the inner structure can be declared inside the block for the outer loop, since they are only needed in that block. Similarly, it is clear that we must initialize the sum to 0 each time we start a cycle through the outer loop, since each cycle is for a different students. If we did not initialize the sum to 0 each time through, then each students' sum (and, hence, average) would include the sum from all the previous students. A program realizing this design is:
| ||||||
Exiting Loops Early | |||||||
|
Often it is useful to be able to stop the execution of a loop in the middle.
There are two different types of actions that may be desirable.
| |||||||
| Skipping The Rest Of One Cycle |
Under some circumstances, there may be a point midway into the body of a loop
in which you would like to skip the remainder of the code in the body of the loop
for that one pass through the loop. That is, you wish to jump directly
to the bottom of the loop and do the increment step. This is
accomplished with the continue statement.
| ||||||
| Breaking out of the loop entirely |
A more common circumstance is a situation in which you wish to abort a loop entirely,
continuing execution with the code after the loop. This is done by using the
statement break (as in the switch statement). This statement
will cause execution of the code in the loop to terminate, and for flow to continue below
the loop.
| ||||||
| Using Labels With Break And Continue |
If you are inside the body of a loop that is itself nested in another loop,
and you wish to continue or break out of the outer loop,
you can use a labelled continue or break statement.
In order to do this, a label is used to mark the beginning
of the loop you might wish to break out of. The label is simply any valid
Java identifier followed by a colon. The name of the label is then given
after the word continue or break that you wish it to apply to.
| ||||||
| Use These In Moderation |
We have intentionally not given examples of using either continue or break.
The reason is that they should only be used in moderation, when the alternative
would be a very contorted piece of code. Under most circumstances,
they can be avoided by choosing a better design (like using an if statement
to avoid the bit of code you wish to skip over, rather than using a continue statement),
or using one of the other loop constructs that will be discussed in the next lecture, instead of using a for loop. There are, however, examples of their use in a number of Java reference books.
| ||||||
Last modified August 28 for Fall 99 cs5 by fleck@cs.hmc.edu
This page copyright ©1998 by Joshua S. Hodas. It was built with Frontier on a Macintosh . Last rebuilt on Sun, Oct 4, 1998 at 1:15:33 PM. | |
http://www.cs.hmc.edu/~hodas/courses/cs5/week_05/lecture/lecture.html | |