Module 6: More Loopiness: while-loops, do-loops, for-each
Objectives
For-loops aren't the only way to write loops. Sometimes
we don't know in advance how many iterations are needed.
By the end of this module, you will be able to:
Demonstrate use of indefinite while loops with input inside.
Demonstrate ability to write code in equivalent loops (for-loop to while-loop and vice versa).
See examples of do-while, continue, and break.
Try the for-each loop.
Identify new syntactic elements related to the above.
6.0 Audio:
6.0 A simple example
Consider this simple problem:
We call Math.random() repeatedly.
Suppose we are interested in getting a number
between 0 and 0.01. It might take
several tries (calls to Math.random()).
The first trial is trial 0, the second one is trial 1, etc.
⇒ We'd like the trial # when we get our desired result.
Here's one attempt:
6.1 Exercise:
Add a println to the above program
to print out each
value of x
generated,
writing your code in
MyRandomCount.java.
Run the above program several different times
(because the output is random). How many trials
are needed to get a random number generated that's
between 0 and 0.01?
6.2 Exercise:
There is a problem with the above program.
To see it, suppose we change the question
to: how many trials
are needed to get a random number generated that's
between 0 and 0.95?
To address the problem, we need stop updating
trial once we get the first "hit"
We can do this using a boolean so-called
"flag" variable:
Here, done is initially false.
The original body of the for-loop is now in
an if-statement that tests done.
The code inside is executed only if done is
false:
This way, when we want to not execute the
code inside, we change done to true.
This way, the if-body does not get executed after that,
even if the for-loop runs on.
6.3 Exercise:
Write this up in
MyRandomCount2.java.
Again, add one println just inside the for-loop
that prints the current values of i and
done. Add another println inside the
if so that each value of x generated
is printed. Change 0.95 to 0.25.
Then execute a few times to see how this is working.
Preventing further execution of the for-loop:
There is a more elegant way to stop further execution of a loop.
We can do this using a break statement:
When the break executes, execution continues
after the for-loop
⇒ It's a way to jump out of a for-loop.
6.4 Exercise:
Try the above program in
MyRandomCount3.java
to see that it works.
Change the problem back to the first one, of seeing
how many trials are needed to generate the first
number less than 0.01.
Again, add a println to print each generated value.
The problem we have not yet solved: what if the
for-loop upper-limit is not enough?
To solve this problem, we will use a while loop:
Here, you can read the while statement as
"as long as (x > 0.01)" ... (execute the loop body).
6.5 Exercise:
Try the above program
MyRandomCount4.java
to see that it works.
Again, add a println inside the loop at the
end, to print each generated value.
Initialization for a while loop:
One has to be careful when initializing the
variables that are tested in a while-loop.
Notice that the following would not work:
int trial = 0;
double x = 0;
while (x > 0.01) {
trial++;
x = Math.random ();
}
System.out.println (trial);
6.6 Exercise:
Why not? Explain the above issue in
module6.pdf
6.7 Audio:
In the sequel, we'll look at some examples of both
while-loops and the break statement.
Rules of thumb about which to use:
When you do NOT know the upper limit, use a while-loop.
When you do, prefer a for-loop (even though you
can still use a while-loop).
If you need to stop a loop after some
condition is true, then use break.
6.1 While-loop examples
Let's start with a simple one: what is the first
square (of an integer) that is larger than 1000?
To solve this, we'll use this high-level idea:
1. Start with i = 0.
2. Check whether i*i > 1000.
3. If not, increment i and repeat.
Here's the while-loop:
Since we know that i can never be larger than 1000 itself,
we could use a for-loop with a break:
6.8 Exercise:
Although it's more complicated, as an exercise,
rewrite the above using a for-loop but
without a break.
Write your program in
MySquare.java.
Consider the one-dimensional random-walk problem:
A particle starts at the point (5,0) on the x-axis.
At each step, it either moves left or right, choosing randomly.
When the particle reaches either the origin (0,0) or
the point (10,0), it stops.
The obvious question: how many steps (moves) until it stops?
It'll take at least 5 steps, but it could take an unknown
(large) number of steps.
⇒ An obvious candidate for a while-loop.
Consider the following program:
Notice that the while-loop condition is an
AND-combination:
Both x>0 and x<10 need to be satisfied
to continue executing inside the while-loop.
Here, we have used a random number between 0
and 1 to decide which way to go.
⇒ Half the time we will move right because half the time,
a random number will be less than 0.5.
6.9 Exercise:
In
MyRandomWalk.java
modify the above code so that you also detect which
"boundary" the particle hits (0 or 10) when
it stops, and print it out. Include a println inside
the while-loop to print the current value of x
so that you see how it's changing.
Next, let's estimate the average number
of steps it takes to stop:
6.10 Exercise:
Why is a for-loop used in main instead of
a while-loop? Just to see how it would work,
re-write the loop in main to use
a while-loop.
Write your code in
MyRandomWalk2.java.
What is the average number of steps reported?
The above idea generalizes quite easily to two dimensions:
Here, we'll place a particle at location (5,5)
and let it wander randomly (on integer coordinates).
The particle stops when it hits the walls of the box
with corners (0,0) and (10,10).
How does the particle move? With equal probability
it moves up, down, left, or right.
⇒ Since there four possibilities, each occurs with probability 0.25.
To decide, we'll draw a random number between 0 and
1:
If the number is between 0 and 0.25, move north.
If it's between 0.25 and 0.5, move south.
If it's between 0.5 and 0.75, move east.
Otherwise, move west.
Here is the structure of the while-loop
6.11 Exercise:
In
MyRandomWalk3.java.
add the missing lines of code to determine the new
values of x and y in the loop. Then, estimate the
average number of steps over 10,000 trials.
6.12 Exercise:
Let's now visualize a random walk.
Download
RandomWalk2D.java
add the missing lines of code from 6.11 to determine the new
values of x and y in the loop.
You will also need
DrawTool.java.
Modify your code so that the walking is endless: when
the particle reaches a boundary, make it bounce back
or take a different direction.
About random walks:
While the idea of random walk might sound like a silly
exercise, it turns out to have surprising explanatory power.
Some important physical processes, such as osmosis, are explained
by random walk models. Other examples include: the spread
of disease, stock price changes.
One of the most famous applications of a random walk
model is Google's search engine.
The mathematics of random walks is fairly difficult, even
for mathematicians.
However, in a few lines of code, we can not only simulate,
we can actually perform estimation. More about this later.
6.13 Audio:
6.2 Examples with break
Consider this problem:
We are given two arrays A and B.
For each element in A, we are to see if
it occurs in B, and if it occurs, report
the position in the array where it occurs.
Thus, for the arrays
int[] A = {1, 9, 16, 25, 36, 49, 64};
int[] B = {2, 4, 8, 16, 32, 64};
we need to report something like
A[2] found at position=3 in B
A[6] found at position=5 in B
Here's the program:
The rule with break - when executed,
it "breaks" out of the nearest enclosing for-loop:
Notice that the break is itself inside a
conditional
⇒ The break can be deeply nested inside a loop.
6.14 Exercise:
Add a println just inside the outer for-loop to print i,
and one just inside the inner for-loop to print j.
Trace the program's execution.
6.15 Exercise:
Suppose we were to solve this variation of the problem:
given the two arrays, see if an element in A
occurs in B and if it occurs, print the position.
Thus, we only need one element to satisfy this.
Modify the program,
writing in
ArraySearch2.java
to make this work.
Here's another simple example:
Suppose we examine a string to locate the position of
the first blank (space). If there are none, we print -1
The break gets executed when i is 5:
Consider this while-loop version:
6.16 Exercise:
Why doesn't this work? Can you tell just by reading?
Suppose you changed initial value to position = 0
outside the while-loop. Does it work?
Answer these questions in
module6.pdf.
6.17 Exercise:
In
MyBlankFinder.java,
fix the problem with the two variations
of a while-loop above, the first variation being the sample
while-loop code above, and the second being the one in the previous exercise.
6.3 Infinite loops
Consider this programming error:
Here, we mistakenly forgot to increment i inside the loop.
⇒ The loop test is always true.
In this case, execution continues forever.
6.18 Exercise:
Don't believe it? Try it (in
MyInfiniteLoop.java)!
Remember: you type Control and C simultaneously at the
command line to stop a program.
Here's another example of an error that leads to an
infinite loop:
Generally, one should be careful with equality (or not-equal)
tests in while-loop conditions.
The following inequality, even if not ideal, works:
6.19 Exercise:
Can you find the flaw in the following program just
by reading? What would be error be?
6.4 The do-while loop
We'll now look at a few less frequently used features
related to loops:
There is a version of the while loop called a do-while:
Here, execution enters the loop directly from above,
without any condition tested:
As long as the condition holds, execution continues
inside the loop:
6.20 Exercise:
In
module6.pdf
trace the execution of the above do-while loop.
6.5 The continue statement
A related statement to the break statement
is the continue statement:
When continue is executed, execution
goes back to the top of the loop, with the increment occurring
followed by the
i<=n test:
This is sometimes useful when the rest of the
loop has complex code. (The above example does not.)
6.21 Exercise:
In
module6.pdf
trace the execution of the above loop.
6.6 Improving an earlier program
In Module 6 of Unit-0, we wrote a program to have
a conversation. It was just a single back-and-forth.
Let's now use a while-loop to let it go as long as desired.
Here's the program:
// Get name.
System.out.println ("What is your name?");
String name = IOTool.readStringFromTerminal (">> ");
// Greet.
System.out.println ("Hi " + name + "! How are you today?");
String response = IOTool.readStringFromTerminal (">> ");
// As long as the user doesn't quit, we'll keep going.
while (! response.equalsIgnoreCase("bye")) {
if (response.endsWith("you?")) {
System.out.println ("Good, thanks for asking. What do you want to talk about?");
}
else if (response.endsWith("?")) {
System.out.println ("That's a good question. Tell me how you feel about that.");
}
else {
System.out.println ("Hmmm. I'd like to know more.");
}
response = IOTool.readStringFromTerminal (">> ");
} // endwhile
System.out.println ("Bye " + name + "! It was nice talking to you");
Note:
The while-loop allows the conversation to continue indefinitely
until the user types "bye".
We've added a tiny comment to demarcate the end of a while-loop.
This is good practice when loops are long enough that you can't
see the closing brace.
Keep in mind that the size of the terminal window can
be expanded.
6.22 Exercise:
Improve the above program perhaps by choosing randomly between
several responses. Report an interesting conversation.
Write your code in
Conversation.java,
and report your interesting conversation in
module6.pdf.
You will need
IOTool.java,
WordTool.java,
and
wordsWithPOSAndPron.txt.
6.7 The for-each loop
One of the most useful variations of a loop is the
so-called for-each loop:
As an example, we'll print all English words that start and
end with the same letter.
Here's the program:
// Get the words as an array:
String[] words = WordTool.getUnixWords ();
// Set up a counter:
int n = 0;
// Note the for-loop:
for (String w: words) {
int lastIndex = w.length() - 1;
if ( w.charAt(0) == w.charAt(lastIndex) ) {
System.out.println (w);
n++;
}
}
System.out.println (n + " such words");
Let's look at little more closely at the for-loop:
for (String w: words) {
}
Here:
Instead of an integer variable that ranges through the
size of the array, we have a variable that
directly ranges through the contents of the array.
The variable w
is our choice (like the
i in
the traditional for-loop).
for (String w: words) {
}
The content-traversing variable w
is followed by a colon.
for (String w: words) {
}
Finally, the array itself is named after the colon.
for (String w: words) {
}
You should read this out aloud as "for each string w in words".
6.23 Exercise:
In
FirstLast.java
type up the above to see how many words satisfy this property.
You will also need to download
words.txt
(You already have
WordTool.java)
6.24 Exercise:
In
FirstLast2.java
identify how many words like "tomato"
have the same first 2 letters as the last 2.
How many words have 3 letters the same (like "hotshot")?
While the for-each loop is generally used with complex types
like strings, one can use it with basic types as well:
Consider this example:
int[] A = {1, 4, 9, 16, 25};
// Standard for-loop:
for (int i=0; i<A.length; i++) {
System.out.println (A[i]);
}
// The for-each loop:
for (int k: A) {
System.out.println (k);
}
Once again, the variable
k
ranges over the content of the array
A
and NOT over the range of array indices.
The for-each syntax is simpler and cleaner.
However, one disadvantage of the for-each loop is that
we can't "get at" the array internals if it's needed.
For example if we want to check whether two successive
elements are identical.
Example:
int[] A = {1, 4, 4, 9, 16, 16, 25};
for (int i=0; i<A.length-1; i++) {
if (A[i] == A[i+1]) {
System.out.println (A[i] + " repeats in succession");
}
}
for (int k: A) {
// Can't get at array locations.
}
6.25 Audio:
6.8 When to use which loop
Rules of thumb:
Prefer a for-each if you can use it.
If that doesn't work, use a standard for-loop.
If that doesn't work, use a standard for-loop with
break.
If that's not a good fit, use a while-loop.
If you have to, use a do-while.
Lastly, there is a version of
break.
called a labelled break that's somewhat rarely used.
We won't say much about it here.
6.9 When things go wrong
6.26 Exercise:
The following program is supposed to create a new string
from an existing one by removing spaces. However, there
are two logical errors. Find and fix them in
WhileLoopError.java.
String s = "The woods are lovely, dark and deep ";
char[] letters = s.toCharArray ();
int k = 0;
String r = "";
while (k < letters.length) {
while (letters[k] == ' ') {
k++;
}
r += letters[k];
}
System.out.println (r);
Hint: insert a println inside each of the loops to help trace
the problem.
6.27 Exercise:
The code below intends to find the highest power of 2 that's
less than a given N. For example, if N=30, then 4 should
be the result because 24 = 16 ≤ 30
while 25 = 32 > 30. Fix the error
in WhileLoopError2.java.
Try N=1, N=30 and N=1000.
int power = 1;
int N = 1000;
int k = 1;
while (power < N) {
power = power * 2;
k ++;
}
System.out.println (k);