3.6. Flow of Control: Control StructuresWe have been ignoring a couple of problems with the definition of the OneRowNim class. One problem is that we would describe a One-Row Nim game as two players taking turns until there are no more sticks. An object using OneRowNim would need a way to repeatedly execute a group of statements. One command in Java that controls the repetition of a block of statements is called a while loop. We will consider it later in this section. A second problem is with the definition of takeSticks(): public void takeSticks(int num) { nSticks - num; player = 3 - player; } It is possible to call this method with an argument greater than 3 or less than 1. The call game.takeSticks(5) will remove five sticks even though the rules of One-Row Nim say that you must remove one, two, or three. While you might assume that the user interface should prevent the user from breaking this rule, it is a far better design to deal with this in OneRowNim. To do so we need a Java structure that executes different statements depending on whether the parameter is greater than 3, less than 1, or between 1 and 3. The Java if-else statement has this capability. A fuller treatment of control structures appears in Chapter 6, but in this section, we will briefly introduce a couple of simple control structures. This will enable us to write programs that take more interesting actions. 3.6.1. The Simple if StatementA selection control structure, allows a program to select between two or more alternative paths of execution. The if statement is the most basic selection control structure in Java. Most programming languages have its equivalent.
Simple if statement Java Language Rule: if Statement
The statement contained in the if statement can be any valid Java statement, including a compound statement. (Recall from Chapter 1 that a compound statement is a set of statements contained within curly braces.) The boolean expression is an expression that is either true or false. We have seen examples of boolean expressions that involve int variables, int values, and the inequality or equality operators. A method call to a method with a boolean result type is another example of a boolean expression. Given this description of if statement syntax, the following are examples of valid if statements: if (true) System.out.println("Hello"); if (nSticks <= 0) System.out.println("game is over"); For readability, we usually write an if statement with its contained statement indented on the next line: if (true) System.out.println("Hello"); if (nSticks <= 0) System.out.println("game is over"); The following are all examples of syntax errors involving the if statement: if true // Parentheses are missing System.out.println("Hello"); if (nSticks <= 0) return // Semicolon missing if ("true") return; // "true" is not a boolean if (true) "Hello"; // "Hello" is not a statement Semantically, the if statement has the following interpretation: First, the boolean condition is evaluated. If it is true, then the contained statement is executed; if it is false, then the contained statement is not executed. This is shown in Figure 3.11. The flowchart clearly shows that program flow will take one or the other of the alternative paths coming out of the diamond-shaped boolean condition box. The branch through the rectangular statement box will be taken when the boolean condition is true; otherwise the statement will be skipped. Figure 3.11. Flowchart of the if statement. Diamond-shaped symbols at the branch points contain boolean expressions. Rectangular symbols can only contain executable statements. Circles act simply as connectors, to connect two or more paths.
As another example, consider the definition of a getPlayerString() method for the OneRowNim class: public String getPlayerString() { if (player == 1) return "Player One"; // Exit the method if (player == 2) return "Player Two"; // Exit the method return "Player error"; // Exit the method } The flowchart in Figure 3.12 shows the program flow of the entire getPlayerString() method. It is important to note that when a return statement is executed in a method, control is returned immediately to the calling method. Thus, if player == 1 is true, the string "Player One" is returned to the calling method and the getPlayerString() method exits at this point. If it is false, then player == 2 should be true (if we have a consistent state) and the string "Player Two" should be returned and the method exited. Thus, if we have a consistent statethat is, if player has value 1 or 2then the third return statement should never be reached. Figure 3.12. Flowchart of the getPlayerString() method.
The following example shows the more common case where the statement contained in an if statement can be a compound statement: if (player == 1) { String s = "Player One"; System.out.print (s); System.out.println (" plays next"); System.out.println (" The game is not over"); }
Compound statement If player == 1 is true, then all four statements in the contained compound statement will be executed. Note here that we are declaring the local variable, s, in this block. Its scope would extend only to the end of the block. Note also that when we use a compound statement, the compound statement itself is not followed by a semicolon because it is already enclosed in braces.
Local scope Forgetting the braces around the compound statement is a common programming error. Merely indenting the statements following the if clause does not alter the logic of the if statement. For example, the following if statement still has only one statement in its if clause: if (condition1) System.out.println("One"); System.out.println("Two"); // Not part of if statement This segment will always print "Two" because the second println() is not part of the if statement. To include it in the if statement, you must enclose both println() statements within braces: if (condition1) { System.out.println("One"); System.out.println("Two"); } Debugging Tip: Indentation
3.6.2. The if-else StatementA second version of the if statement incorporates an else clause into the structure. This allows us to execute either of two separate statements (simple or compound) as the result of one boolean expression. For example, the statement if (player == 1) System.out.println("Player One"); else System.out.println("Player Two"); will print "Player One" if player == 1 is true. Otherwise, it will print "Player Two." Java Language Rule: If-else Statement
As in the case of the simple if statement, the keyword if is followed by a parenthesized boolean expression, which is followed by statement1, which may be either simple or compound. If statement1 is a simple statement, then it is followed by a semicolon. The else clause follows immediately after statement1. It begins with the keyword else, which is followed by statement2, which can also be either a simple or compound statement. Note that there is no boolean expression following the else keyword. In an if-else statement, the boolean expression following the keyword if goes with both the if and else clauses.
If-else syntax Semantically, the if-else statement has the following interpretation: If the boolean expression is true, execute statement1; otherwise execute statement2. This interpretation is shown in Figure 3.13. Figure 3.13. Flowchart of the if-else statement.
3.6.3. The Nested if-else Multiway Selection StructureThe statements that one inserts in place of statement1 and statement2 in the if-else statement can be any executable statement, including another if statement or if-else statement. In other words, it is possible to embed one or more if-else statements inside another if-else statement, thereby creating a nested control structure. As with most things, making a control structure too complex is not a good idea, but there is a standard nested if-else control structure that is very useful. It is known as multiway selection. As shown in Figure 3.14, the multiway structure is used when you want to select one and only one option from several alternatives. Figure 3.14. Flowchart of a nested if-else statement. |
Exercise 3.10 | Consider the following method with boolean parameter. public String getStatus(boolean isDone) { if (isDone) return "Done"; else return "Not Done"; } Draw a flowchart for the if-else version of the getStatus() method, using the figures in this section as a guide. The if-else structure should be drawn exactly as shown in Figure 3.11. It should have a single entry point that leads directly to the top of a diamond-shaped box that contains a boolean condition. There should be two branches coming out of the condition box. The one going to the right is the true case, and the one going to the left is the false case. Each of these branches should contain one rectangular box, which contains the statements that would be executed in that case. The left and right branches should be connected by a circular symbol that is aligned directly under the diamond box whose conditions it connects. There should be a single exit arrow pointing directly down.
Flowchart symbols |
Exercise 3.11 | Identify the error in the following statements: if (isHeavy == true) System.out.println("Heavy"); else ; System.out.println("Light"); if (isLong == true) System.out.println("Long") else System.out.println("Short"); |
Exercise 3.12 | Suppose we have an int instance variable named player in some class describing a three-person game. Write a method named getPlayerName() that returns a String. It should return "Ann," "Bill," "Cal," or "Error" when the value of player is respectively 1, 2, 3, or any other value. |
Exercise 3.13 | How does a parameter for a primitive type differ from a parameter for a reference type? |
A repetition structure is a control structure that repeats a statement or sequence of statements in a controlled way. Repetition structures are also referred to as loop structures. Many types of programming tasks require a repetition structure. Consider some examples.
You want to add up the squares of the numbers from 1 to 100.
You want to compute compound interest on an amount of money in a savings account with a fixed interest rate if it is kept there for 30 years.
A computer security employee wants to try every possible password in order to break into the account of a suspected spy.
You want to have players input moves for a turn in a game until the game is over. Our OneRowNim is an example.
We will study several different repetition structures of Java in depth in Chapter 6. We will briefly consider the while statement here so as to be able to define more powerful and more interesting methods. Let us write a method that solves a slight generalization of the first problem above. We will use the while statement to sum the squares of integers from 1 to a number specified as a parameter of the method. Thus, the method call sumSquares(3) should return the value 14, since 1 * 1 + 2 * 2 + 3 * 3 = 1 + 4 + 9 = 14.
public int sumSquares(int max) { int num = 1; int sum = 0; while (num <= max) { // While num <= max sum = sum + num*num; // Add square to sum num = num + 1; // Add 1 to num } // while return sum; // Return the sum }
In this example, the variable num is assigned an initial value of 1 before the while statement. The boolean expression num <= max in parentheses after while states the condition for which we wish to continue summing squares. Finally, the last statement in the block following the boolean expression adds 1 to numthat is, this variable is updated at the end of the block.
The while statement is a loop statement in which the loop entry condition occurs before the loop body. It has the following general form:
Java Language Rule: While Statement
The while statement has the following syntax:
|
When the while statement is executed, the loop entry condition is evaluated, and if it evaluates to false, execution continues at the statement immediately after the loop body. If the loop entry condition evaluates to true, the loop body is executed and then the entry condition is evaluated again. The loop body continues to be executed until the loop entry condition evaluates to false.
To have a while statement accomplish a task, the variable or variables in the loop entry condition must be initialized correctly before the while statement, and these variables must be correctly updated at the end of the loop body. We can refer to the initializer statement followed by a while statement as a while structure. We can restate the above guidelines as a design principle:
Effective Design: Loop Structure
A properly designed while structure must include an initializer, a loop entry condition, and an updater. The updater should guarantee that the loop entry condition eventually fails, thereby allowing the loop to terminate. |
In pseudocode, the while structure would take the following form:
InitializerStatements; // Initializer while (loop entry condition) // Bound test { Statements; // Loop body UpdaterStatements; // Updater }
As its form suggests, the while structure is designed so that on some conditions the loop body will never be executed. Because it tests for the loop entry condition before the loop body, it is possible that the loop body may never be executed. We might say that the structure is designed to perform zero or more iterations.
For example, if the method call sumSquares(-3) is executed, the loop body will be skipped, because the loop entry condition num <= max is false to begin with. No iterations will be performed, and the algorithm will simply return the value 0.
Note also that the bound test in the while statement is preceded by initializer statements, and the loop body contains updater statements. The semantics of the while structure are shown in Figure 3.15.
Exercise 3.14 | Modify the definition of the sumSquares() method to define a method named sumCubes() that sums the cubes of integers from a minimum value up to a maximum value and returns the sum. sumCubes() should have two parameters that will store the minimum and maximum values. Thus the method call sumCubes(2,3) should return 35, since 2 * 2 * 2 + 3 * 3 * 3 = 8 + 27 = 35. |