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:

 

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);
     
 

6.28 Audio:




On to Module 7



© 2017, Rahul Simha