CS
99
Summer
2001������������������������������������������������������������������������������ ������ 7.16
Lecture Notes 5
Combining Control Structures
�
An
algorithm can contain many control-flow constructs in non-trivial combinations.
�
Sequencing,
branching, and looping can be interleaved and nested within each other.
�
For
example, algorithms can contain nested loops.�
A loop inside a loop can take on the form �do A exactly N times� where A
itself is of the form �Repeat B until Q�
�
The
A part of the outer loop can contain many further segments each of which can,
in turn, employ additional sequencing, branching, and looping constructs, and
the same goes for the inner loop.� Thus
there is no limit to the potential intricacy of one�s algorithms.
�
Experienced
programmers often have �templates� mentally available for particular kinds of
problems.� Some kinds of situations show
up so often that it�s useful to carry these patterns of code around in your
head, so that when these situations arise, the program (or at least parts of
it) can more or less write itself.
�
For
instance, many of you are finding Lab 6 a bit challenging.� It really isn�t (in my opinion) because the
different parts of the lab are really asking you to do the same things over and
over; once you grasp the pattern that emerges, most of you should fly through
the assignment.
�
What
are some of the kinds of patterns tested in Lab 6?
A pattern for iterating n
times:
/* Do whatever n times: Version 1 */
int j = 1;
while ( j <= n ) {
���� whatever
���� j = j + 1;
}
�
Think
of such a pattern as a whole: �do whatever
n times�
�
whatever can refer to variable or it
may not
�
There�s
nothing special about these variables: any variable can be used instead of j
and n.
�
j
is a counter variable: it�s purpose is make sure the loop body executes
precisely n times.
�
Note
that it isn�t necessary to count from j = 1 to j = n, nor do we need to count
upwards all the time.� The following
code snippets will work just as well for iterating something n times:
/* Do whatever n times : Version 2*/
int j = 0;
while ( j < n ) {
���� whatever
���� j = j + 1;
}
/* Do whatever n times: Version 3*/
int j = n;
while ( j > 0 ) {
���� whatever
���� j = j - 1;
}
�
Note
carefully the stopping conditions in these new versions.� In Version 2, because j is starting at 0 and
not 1, the stopping condition must be strictly j < n, and not j<= n.� If we go all the way to n, then starting
from 0, we will have counted n+1 times, not n times.
�
The
same is true for Version 3.� The
stopping condition must be j > 0 and not j>=0.
/* Do whatever
repeatedly until the user enters -1 */
TokenReader in = new
TokenReader( System.in );
int val =
in.readInt();
while ( val != -1 ) {
���� whatever
���� val = in.readInt();
}
�
In
this circumstance, we don�t use, nor do we need, a counter variable to stop the
loop.� That�s because we don�t know how
long the input will actually be.
�
We
may introduce counter variables to keep track of other things you might be
interested in, but we don�t need one to stop the loop itself. (Indeed, in the
Hi-Lo Game from Lab 6, you use a counter variable to keep track of the number
of guesses the user has made.� But that
counter variable is not used to stop the game.)
�
Since
our loop doesn�t have a counter variable, the loop body must contain a statement
that affects the loop condition in some other way (it must, otherwise, the loop will never end).� In this case, instead of having j count up
until it hits n, we use TokenReader to keep reading in values. Each value
entered is checked against the stopping value in the Boolean conditional.� When the correct stopping value is read, the
loop ends.
�
The
algorithm is not guaranteed to end!� If
the user never enters �1, the loop will not stop.
�
Again
think of such a pattern as a whole: �do whatever an unknown number of times
until a particular stopping value is read�
�
Usually
we don�t think of the stopping value as a part of the input data itself.� It�s considered separately from the data,
it�s merely a stopping condition.� For
instance, in the code above, if the user immediately enters �1 as the first
value, the loop body never execute; �whatever� will not be performed.
�
When
you choose the stopping condition, try to choose some value that�s outside of
the data range you�re looking at.� So if
you�re reading in positive numbers, let your stopping value be negative.�
�
It
also isn�t necessary to have just one
stopping value (and usually it isn�t).�
For instance, if the code snippet above was for reading in non-negative
values (including 0) for the data, we can use the following bit of code just as
well:
/* Do whatever
repeatedly until the user enters -1 */
TokenReader in = new
TokenReader( System.in );
int val =
in.readInt();
while ( val >= 0 )
{
whatever
����
val = in.readInt();
}
�
This
is actually preferable to the previous version because, assuming whatever is expecting only
non-negative numbers, this loop condition guarantees that whatever will never encounter a
negative number (the one before would have kept going if the user had entered
in say �10).
Problem 1:
An integer n is given as input data.� Output a list of the perfect squares between
1 and n^2 inclusive.
Solution 1:
int n = in.readInt();
int j = 1;
/* Output on each line the perfect squares between 1 and n^2 inclusive */
while ( j<= n) {
���� /* Output the perfect square of j */
System.out.println(
j*j );
����
j = j+1;
}
The output for this program segment for an input of 5
is:
1
4
9
16
25
Comments:
�
Here,
the whatever is output the perfect
square of j, the counter variable.
�
Note
that 36 is not printed because j gets the value 6 after it�s printed 25 on the
screen, and the loop ends before it reads the println() statement.
Problem 2:
An integer m is given as input data.� Output a line of m asterisks (*�s).
Solution 2:
int m = in.readInt();
int j = 1;
/* Output a line of m asterisks */
while ( j<= m) {
���� /* Output an asterisk, leave the
cursor on the same line */
System.out.print(
�*� );
����
j = j+1;
}
For an input of 5, the output for this segment is
*****
Comments:
�
Here
the whatever is to print a single
asterisk and leave the cursor on the same line.�
�
Notice
that the stars are all on one line because we�re using a print() statement
instead of a println() statement.
Problem 3 (perfect numbers
again!):
A number is �perfect� if it equals the sum of its
divisors, including 1 but excluding itself.�
An integer p is given as input data.�
Output �perfect� if it�s perfect, and �not perfect� if it�s not.
Solution 3.
int p = in.readInt();
int sum = 0;
int j = 1;
while ( j < p ) {
/* if j divides p
add it to the sum of the divisors so far */
�����
if (p %j == 0 )
���� sum+=j;
j++;
}
Comments:
�
At
first blush, this doesn�t seem like a candidate for any of our patterns.� But it is, because in order to find out the
sum of the divisors, we have to loop through all the possible numbers from 1 to
p-1 to see which ones actually divide p.
�
Hence,
we have to do something (in this
case, does the number divide p? if so, add it to the running sum) a set number
of times (p-1).� The thing that makes
this one more interesting than the previous two is that the whatever part involves the counter
variable, j, itself.
Problem 4:
An integer n is given as input data.� Output a triangle of asterisks with base and
height n, e.g., if n is 3, the following is output on the screen:
*
**
***
Solution 4:
int n = in.readInt();
int j = 1;
while ( j<= n ) {
���� /* output a line of j *�s */
int k = 1; while ( k <= j
) { ���� System.out.print(� * � ); ���� k = k+1; }
���������
j
= j+1;
}
�
Comments:
�
This
one is more involved because whatever
is itself a loop.�
�
But
notice! The whatever part is written
in precisely the same way as the outer loop, because it�s doing the same
thing!� In the whatever part we�re doing something a set number of times: in fact,
we�re printing an asterisk j times! Well, isn�t that just how we described the
pattern for doing whatever n times?
Now the n is a j, but that�s no biggie! (What�s
in a name? That which we call a rose by any other name would still smell the
same � )