Chapter 14

Creating Loops within Loops

IN THIS CHAPTER

check Analyzing loop strategies

check Diagnosing loop problems

check Creating nested loops

If you’re an editor at John Wiley & Sons, Inc., please don’t read the next few paragraphs. In the next few paragraphs, I give away an important trade secret (something you really don’t want me to do).

I’m about to describe a surefire process for writing a best-selling For Dummies book. Here’s the process:

Write several words to create a sentence. Do this several times to create a paragraph.

Repeat the following to form a paragraph:
Repeat the following to form a sentence:
Write a word.

Repeat the previous instructions several times to make a section. Make several sections and then make several chapters.

Repeat the following to form a best-selling book in the For Dummies series:
Repeat the following to form a chapter:
Repeat the following to form a section:
Repeat the following to form a paragraph:
Repeat the following to form a sentence:
Write a word.

This process involves a loop within a loop within a loop within a loop within a loop. It’s like a verbal M.C. Escher print. Is it useful, or is it frivolous?

Well, in the world of computer programming, this kind of thing happens all the time. Most five-layered loops are hidden behind method calls, but two-layered loops within loops are everyday occurrences. So this chapter tells you how to compose a loop within a loop. It’s very useful stuff.

By the way, if you’re a Wiley editor, you can start reading again from this point onward.

Paying Your Old Code a Little Visit

The program in Listing 12-5 (over in Chapter 12) extracts a username from an email address. For example, the program reads

[email protected]

from the keyboard, and writes

John

to the screen. Let me tell you, in this book I have some pretty lame excuses for writing programs, but this simple email example tops the list! Why would you want to type something on the keyboard, only to have the computer display part of what you typed? There must be a better use for code of this kind.

Sure enough, there is. The BurdBrain.com network administrator has a list of 10,000 employees’ email addresses. More precisely, the administrator’s hard drive has a file named email.txt. This file contains 10,000 email addresses, with one address on each line, as shown in Figure 14-1.

image

FIGURE 14-1: A list of email addresses.

The company’s email software has an interesting feature. To send email within the company, you don’t need to type an entire email address. For example, to send email to John, you can type the username John instead of [email protected]. (This @BurdBrain.com part is called the host name.)

The company’s network administrator wants to distill the content of the email.txt file. She wants a new file, usernames.txt, that contains usernames with no host names, as shown in Figure 14-2.

image

FIGURE 14-2: Usernames extracted from the list of email addresses.

Reworking some existing code

To solve the administrator’s problem, you need to modify the code in Listing 12-5. The new version gets an email address from a disk file and writes a username to another disk file. The new version is in Listing 14-1.

LISTING 14-1 From One File to Another

import java.util.Scanner;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintStream;

class ListOneUsername {

public static void main(String args[]) throws FileNotFoundException {

Scanner diskScanner = new Scanner(new File("email.txt"));
PrintStream diskWriter = new PrintStream("usernames.txt");
char symbol;

symbol = diskScanner.findWithinHorizon(".",0).charAt(0);

while (symbol != ’@’) {
diskWriter.print(symbol);
symbol = diskScanner.findWithinHorizon(".",0).charAt(0);
}

diskWriter.println();

diskScanner.close();
diskWriter.close();
}
}

Listing 14-1 does almost the same thing as its forerunner in Listing 12-5. The only difference is that the code in Listing 14-1 doesn’t interact with the user. Instead, the code in Listing 14-1 interacts with disk files.

Running your code

Here’s how you run the code in Listing 14-1:

  1. Create a file named email.txt in your Eclipse project directory.

    In the email.txt file, put just one email address. Any address will do, as long as the address contains an @ sign.

  2. Put the ListOneUsername.java file (the code from Listing 14-1) in your project’s src/(default package) directory.
  3. Run the code in Listing 14-1.

    When you run the code, you see nothing interesting in the Console view. What a pity!

  4. View the contents of the usernames.txt file.

    If your email.txt file contains [email protected], the usernames.txt file contains John.

For more details on any of these steps, see the discussion accompanying Listings 13-2, 13-3, and 13-4, over in Chapter 13. (The discussion is especially useful if you don’t know how to view the usernames.txt file’s contents.)

Creating Useful Code

The previous section describes a network administrator’s problem — creating a file filled with usernames from a file filled with email addresses. The code in Listing 14-1 solves part of the problem — it extracts just one email address. That’s a good start, but to get just one username, you don’t need a computer program. A pencil and paper do the trick.

Don’t keep the network administrator waiting any longer. In this section, you develop a program that processes dozens, hundreds, and even thousands of email addresses from a file on your hard drive.

First, you need a strategy to create the program. Take the statements in Listing 14-1 and run them over and over again. Better yet, have the statements run themselves over and over again. Fortunately, you already know how to do something over and over again: You use a loop. (See Chapter 12 for the basics on loops.)

Here’s the strategy: Take the statements in Listing 14-1 and enclose them in a larger loop:

while (not at the end of the email.txt file) {
Execute the statements in Listing 14-1
}

Looking back at the code in Listing 14-1, you see that the statements in that code have a while loop of their own. So this strategy involves putting one loop inside another loop:

while (not at the end of the email.txt file) {
//Blah-blah

while (symbol != ’@’) {
//Blah-blah-blah
}

//Blah-blah-blah-blah
}

Because one loop is inside the other, they’re called nested loops. The old loop (the symbol != ’@’ loop) is the inner loop. The new loop (the end-of-file loop) is called the outer loop.

Checking for the end of a file

Now all you need is a way to test the loop’s condition. How do you know when you’re at the end of the email.txt file?

The answer comes from Java’s Scanner class. This class’s hasNext method answers true or false to the following question:

Does the email.txt file have anything to read in it (beyond what you’ve already read)?

If the program’s findWithinHorizon calls haven’t gobbled up all the characters in the email.txt file, the value of diskScanner.hasNext() is true. So, to keep looping while you’re not at the end of the email.txt file, you do the following:

while (diskScanner.hasNext()) {
Execute the statements in Listing 14-1
}

The first realization of this strategy is in Listing 14-2.

LISTING 14-2 The Mechanical Combining of Two Loops

/*
* This code does NOT work (but you learn from your mistakes).
*/

import java.util.Scanner;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintStream;

class ListAllUsernames {

public static void main(String args[]) throws FileNotFoundException {

Scanner diskScanner = new Scanner(new File("email.txt"));
PrintStream diskWriter = new PrintStream("usernames.txt");
char symbol;

while (diskScanner.hasNext()) {
symbol = diskScanner.findWithinHorizon(".",0).charAt(0);

while (symbol != ’@’) {
diskWriter.print(symbol);
symbol = diskScanner.findWithinHorizon(".",0).charAt(0);
}

diskWriter.println();
}

diskScanner.close();
diskWriter.close();
}
}

When you run the code in Listing 14-2, you get the disappointing response shown in Figure 14-3.

image

FIGURE 14-3: You goofed.

How it feels to be a computer

What’s wrong with the code in Listing 14-2? To find out, I role-play the computer. “If I were a computer, what would I do when I execute the code in Listing 14-2?”

The first several things that I’d do are pictured in Figure 14-4. I would read the J in John, and then write the J in John, and then read the letter o (also in John).

image

FIGURE 14-4: Role-playing the code in Listing 14-2.

After a few trips through the inner loop, I’d get the @ sign in [email protected], as shown in Figure 14-5.

image

FIGURE 14-5: Reaching the end of the username.

Finding this @ sign would jump me out of the inner loop and back to the top of the outer loop, as shown in Figure 14-6.

image

FIGURE 14-6: Leaving the inner loop.

I’d get the B in BurdBrain and sail back into the inner loop. But then (horror of horrors!) I’d write that B to the usernames.txt file. (See Figure 14-7.)

image

FIGURE 14-7: The error of my ways.

There’s the error! You don’t want to write host names to the usernames.txt file. When the computer found the @ sign, it should have skipped past the rest of John’s email address.

At this point, you have a choice. You can jump straight to the corrected code in Listing 14-3 (a couple of sections from here), or you can read on to find out about the error message in Figure 14-3.

Why the computer accidentally pushes past the end of the file

Ah! You’re wondering why Figure 14-3 has that nasty error message.

I role-play the computer to help me figure out what’s going wrong. Imagine that I’ve already role-played the steps in Figure 14-7. I shouldn’t process the first letter B (let alone the entire BurdBrain.com host name) with the inner loop. But unfortunately, I do.

I keep running and processing more email addresses. When I get to the end of the last email address, I grab the m in BurdBrain.com and go back to test for an @ sign, as shown in Figure 14-8.

image

FIGURE 14-8: The journey’s last leg.

Now I’m in trouble. This last m certainly isn’t an @ sign. So I jump into the inner loop and try to get yet another character. (See Figure 14-9.) The email.txt file has no more characters, so Java sends an error message to the computer screen. (Refer to the NullPointerException error message in Figure 14-3.)

image

FIGURE 14-9: Trying to read past the end of the file.

technicalstuff Here’s why I get a NullPointerException: The email.txt file has no more characters, so the call to findWithinHorizon(".",0) comes up empty. (There’s nothing to find.) In Java, a more precise way of describing that emptiness is with the word null. The call findWithinHorizon(".",0) is null, so pointing to a character that was found (charAt(0)) is a fruitless endeavor. Thus, Java displays a NullPointerException message.

Solving the problem

Listing 14-3 has the solution to the problem described with Figures 14-1 and 14-2. The code in this listing is almost identical to the code in Listing 14-2. The only difference is the added call to nextLine. When the computer reaches an @ sign, this nextLine call swallows the rest of the input line without actually tasting it. (The nextLine call gets the rest of the email address, but doesn’t output that part of the email address. The idea works because each email address is on its own separate line.) After gulping down @BurdBrain.com, the computer moves gracefully to the next line of input.

LISTING 14-3 That’s Much Better!

/*
* This code is correct!!
*/

import java.util.Scanner;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintStream;

class ListAllUsernames {

public static void main(String args[]) throws FileNotFoundException {

Scanner diskScanner = new Scanner(new File("email.txt"));
PrintStream diskWriter = new PrintStream("usernames.txt");
char symbol;

while (diskScanner.hasNext()) {
symbol = diskScanner.findWithinHorizon(".",0).charAt(0);

while (symbol != ’@’) {
diskWriter.print(symbol);
symbol = diskScanner.findWithinHorizon(".",0).charAt(0);
}

diskScanner.nextLine();
diskWriter.println();
}

diskScanner.close();
diskWriter.close();
}
}

To run the code in Listing 14-3, you need an email.txt file — a file like the one shown earlier, in Figure 14-1. In the email.txt file, type several email addresses. Any addresses will do, as long as each address contains an @ sign and each address is on its own separate line. Save the email.txt file in your project directory along with the ListAllUsernames.java file (the code from Listing 14-3). For more details, see the discussion accompanying Listings 13-2, 13-3, and 13-4 in Chapter 13.

With Listing 14-3, you’ve reached an important milestone. You’ve analyzed a delicate programming problem and found a complete, working solution. The tools you used included thinking about strategies and role-playing the computer. As time goes on, you can use these tools to solve bigger and better problems.

tryitout Nothing is more challenging for novice programmers than creating nested loops. That’s why I cover looping techniques in this chapter, and in the next two chapters. You might say that I loop through my coverage of programming loops.

Try writing the code that I suggest in the next few paragraphs. Don’t be afraid to make lots of mistakes. If you get stuck, slow down, take a step back, and think about what the computer will do when it follows instructions to the letter.

The solutions are on my web page at www.allmycode.com/BeginProg. But don’t jump to the solutions until you’ve experimented with lots of different ideas. Follow this tried-and-true formula:

Write some code;
Run your code;
while (your program doesn’t work correctly) {
Step through your code, one statement after another, keeping track
of the values of the variables and the computer’s output as
Java follows your instructions exactly as they’re written;
In the step by step execution of statements, notice the place where
Java does something that you don’t want it to do;
Ask yourself how you’d change the statements so that Java would do
what you want it to do;
Change the statements in your code;
Run your code again;
}

END OF THE ROAD

A file named input.txt contains only four characters:

Java

What’s the output when you run the following code?

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;

public class Main {

public static void main(String[] args) throws FileNotFoundException {
Scanner diskScanner = new Scanner(new File("input.txt"));

while (diskScanner.hasNext()) {
char symbol = diskScanner.findWithinHorizon(".", 0).charAt(0);
System.out.print(Character.toUpperCase(symbol));
}
diskScanner.close();
}

}

SEEING STARS

In this chapter’s “How it feels to be a computer” section, I examine each line of a program’s code and ask myself what the computer does when it executes that line. Do the same thing with the following program:

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;

class ReadStars {

public static void main(String args[]) throws FileNotFoundException {

Scanner diskScanner = new Scanner(new File("input.txt"));
char symbol;

while (diskScanner.hasNext()) {
symbol = diskScanner.findWithinHorizon(".",0).charAt(0);

while (symbol == ’*’) {
System.out.print(symbol);
symbol = diskScanner.findWithinHorizon(".",0).charAt(0);
}

System.out.println();
}

diskScanner.close();
}
}

What happens when the input.txt file contains the following characters?

*****x***y*****z

LOOP SOUP

Make a chart to keep track of the changes to the values of i and j as Java executes the following code.

int i = 5;
int j;
while (i > 0) {
System.out.println(i);
i--;
j = 3;
while (j > 0) {
System.out.print(j);
j--;
}
System.out.println();
}

Based on the values in your chart, what will be the output of the code? Run the code in Eclipse to find out whether your prediction is correct.

MAKE SOME CHANGES AROUND HERE

Modify the code from the previous experiment ("Loop soup") so that the output has no lines containing the 321 digit sequence. In place of the 321 lines, the output has lines containing the 123 digit sequence.

SEEING STARS

This experiment comes in two parts. The first part requires only one loop. The second part requires nested loops.

  • Write a program that asks the user how many stars to display. When the user enters a number, the program displays that many stars. Here’s a sample run:

    How many stars? 5
    *****

  • Write a program that repeatedly asks whether the user wants to see a row of stars. As long as the user replies with the letter y, the program does what it did in the previous bullet. That is, the program asks the user how many stars to display and then displays that many stars. As soon as the user replies with the letter n, the program stops running.

    Here’s a sample run of the program:

    Do you want a row of stars? (y/n) y
    How many stars? 5
    *****
    Do you want a row of stars? (y/n) y
    How many stars? 2
    **
    Do you want a row of stars? (y/n) y
    How many stars? 8
    ********
    Do you want a row of stars? (y/n) n

    To create this program, take the code that you wrote in the previous bullet (Part 1 of “Seeing stars”) and surround some of that code inside a second loop.

APROPOS OF NOTHING

In Figure 14-3, the run of a Java program throws a NullPointerException. These NullPointerException messages are never fun, but the more of these messages you encounter, the less frightening they are.

To help desensitize you to NullPointerException messages, generate one of them intentionally. Type the following two lines, one after another, in the JShell window:

String name = null;
System.out.println(name.length());

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.30.178