4. Selection

Life is a sum of all your choices.

— Albert Camus

4.1. Problem: Monty Hall simulation

There’s a famous mathematical puzzle called the Monty Hall problem, based on the television show Let’s Make a Deal hosted by the eponymous Monty Hall. In this problem, you’re presented with three doors. Two of the three doors have junk behind them. One randomly selected door conceals something like a pile of gold. If you can choose that door, you win the gold. After you make an initial choice, Monty, who knows which door the pile of gold is behind, will open one of the two other doors, always picking a door with junk behind it. If you chose the gold door, Monty will pick between the two junk doors randomly. After opening a door, Monty gives you a chance to switch to the other unopened door. You decide to switch or not, the appropriate door is opened, and you win either junk or a pile of gold, depending on your luck.

As it turns out, it’s always a better strategy to switch doors. If you keep your initial choice, you have a 1 in 3 chance to win the gold. However, if you switch doors, you’ll have a 2 in 3 chance. The problem is counterintuitive and leads many people, including mathematicians and people holding advanced degrees, to the incorrect answer.

Think about it this way: Suppose you could pick two doors to open, and if the gold was behind either one of them, you’d win. Clearly, you’d have a 2 in 3 chance of winning. Monty allows you this option. Just pick the two doors you want and tell Monty the third. He reveals one of your two initial doors as junk, and you switch to the other one.

If you still aren’t convinced, that’s fine. Your goal is to write a program that simulates the Monty Hall dilemma, allowing a user to guess a door and then potentially switch. Once you’ve written the simulation, you can choose to play repeatedly and see how well you do if you switch.

A Monty Hall scenario has two significant features that distinguish it from problems in previous chapters. First, randomness play a role. Generating random numbers has become an important part of computer science, and most languages provide programmers with tools for generating random or practically random numbers. Recall the Random class from Chapter 3. With an object of type Random called random, you can generate a random int between 0 and n - 1 by calling random.nextInt(n).

The second and much more important feature of Java in the solution to this problem is the element of choice. A random door is chosen to hide gold, and the program must react appropriately. The user chooses a door, and the program must carefully choose another door to open in response. Finally, the user must decide whether or not he or she wants to switch his or her choice. Inherent in this problem is the idea of conditional execution. Every program from the previous chapter runs sequentially, line by line. With conditional execution, only some of the code may be executed, depending on input from the user or the values that random numbers take. Previously, every program was deterministic, a series of inevitable consequences. Now, that linear, one-thing-follows-another paradigm has split into complex trees and webs of possible program executions.

4.2. Concepts: Choosing between options

Before we get to random numbers and the complex choices involved in the Monty Hall problem, let’s talk about the simplified approach that most programming languages take to conditional execution. When we come to a point in a program where there is a choice to be made, we can think of it as the question, “Do I want to perform this series of tasks?” Like the classic game of 20 Questions, these questions in Java generally only have two answers: “yes” or “no.” If the answer is “yes,” the program completes Task A, otherwise it completes Task B. It’s easier to design programming languages that can handle yes-or-no questions than any general question. If you’ve studied logic in the past, you have probably run across Boolean logic. Boolean logic gives a set of rules, similar to the rules of traditional algebra, that can be applied to a system with only two values: true and false.

4.2.1. Simple choices

Because we want to build a system using only yes-or-no questions, Boolean logic is a perfect fit for computer science. To conform with other computer scientists, we try to think of conditions in terms of true and false, instead of yes and no. Thus, we can begin to formulate the kinds of choices we want to make:

If it’s raining outside,

I’ll take my umbrella.

This statement is a very simple program, even though it’s not one executed by a computer. The person following this program asks herself, “Is it raining today?” If the answer is “yes,” then she’ll take her umbrella. We can abstract this idea a bit further by saying that raining outside is a condition p and that taking my umbrella is an action a. In other words, if p is true, then do a. We haven’t specified what is to be done if p is not true, although we can assume that the actor in this drama will not take an umbrella.

If we want to view p as a decision to make, we can specify what happens if it’s not true. For example, we could formulate another choice:

If I have at least $50 in my pocket,

I’ll eat a lobster dinner;

otherwise,

I’ll eat fast food.

In this case, we let having at least $50 be condition q, eating a lobster dinner be action b, and eating fast food be action c. Now we’ve created a decision. If q is true, the person will do action b, but if it’s false, she’ll do action c.

4.2.2. Boolean operations

Even by itself, the ability to pick between two options is powerful, but we can augment this ability in a couple of ways. First, we don’t have to rely on simple conditions. Using Boolean logic, we can make arbitrarily complex conditions.

If I’m bored, or it’s late and I can’t sleep,

I’ll watch television.

Someone following this program will watch television if he’s bored or if it’s late and he also can’t sleep. We can break the condition into three sub-conditions: I’m bored is condition x, it’s late is condition y, and I can’t sleep is condition z. We have connected these three conditions together using the words “and” and “or.” These two simple words represent powerful concepts in Boolean logic, AND and OR. When two conditions are combined with AND, the result is true only if both conditions are true. When two conditions are combined with OR, the result is true if either of the conditions is true.

We can create a table called a truth table to show all the possible values certain conditions can take. We’re going to use the symbol ∧ to represent the concept of AND and the symbol ∨ to represent the concept of OR. We’ll also abbreviate true to T and false to F.

Given a condition x, a condition y, and the condition made by xy, this truth table shows all possible values. As stipulated, xy is true only when both x and y are true.

x y xy

T

T

T

T

F

F

F

T

F

F

F

F

This truth table gives all the values for xy. As you can see, xy is true if x or y are true.

x y xy

T

T

T

T

F

T

F

T

T

F

F

F

There’s confusion surrounding the word “or” in English. Sometimes “or” is used in an exclusive sense to mean one or the other but not both, as in, “Would you like lemonade or iced tea with your meal?” In logic, this exclusive or exists as well and is called XOR. This difference gives another reason for a formally structured language like mathematics or Java to express ourselves precisely. When two conditions are connected with XOR, the result is true if one or the other but not both conditions are true. We use the symbol ⊕ to represent the XOR operation in the truth table below.

This truth table gives all the values for xy.

x y xy

T

T

F

T

F

T

F

T

T

F

F

F

The operations AND, OR, and XOR are all binary operations like addition and multiplication. They connect two conditions together to get a result. There’s also a single unary operation in Boolean logic, the NOT operator. A NOT simply reverses a condition. If a condition is true, then NOT applied to that condition will yield false, and vice versa.

Here’s a truth table for NOT, using the symbol ¬ to represent the NOT operation.

x ¬x

T

F

F

T

Now that we’ve nailed down some notation for Boolean logic, we can express the complicated expression that sent us down this path in the first place. Recall that x is I’m bored, y is it’s late, and z is I can’t sleep. Let d be the action I’ll watch television. We can express the choice in this way: If x ∨ (yz), then do d. Using this notation, we’ve expressed precisely the conditions for watching television, using parentheses to clear up the ambiguity present in the original statement. If we can map individual conditions to Boolean variables, we can build conditions of arbitrary complexity.

4.2.3. Nested choices

Making one choice is all well and good, but in life and computer programs, we may have to make many interrelated choices. For example, if you choose to eat at a seafood restaurant, then you might choose between eating shrimp and lobster, but if you choose instead to eat at a steakhouse, the options of shrimp and lobster might not be available.

A nested choice is one that sits inside of another choice you’ve already made. We could describe choices of restaurants and meals as follows.

If I want seafood,

I’ll eat at Sharky’s, where

if I have at least $50,

I’ll order the lobster;

otherwise,

I’ll order the shrimp.

But if I don’t want seafood,

I’ll eat at the Golden Calf, where

if I have at least $30,

I’ll order the filet mignon;

otherwise,

I’ll order the pork chops.

The previous description is long, but it precisely expresses the decisions our imaginary diner might make. This description in English has drawbacks: It’s long and repetitive, and the grouping of specific meal choices with specific restaurants isn’t clear.

In the next section, we discuss Java syntax that allows us to express the same sorts of decision patterns. Unlike English, Java has been designed to make these sequences of decisions clear and easy to read.

4.3. Syntax: Selection in Java

With some theoretical background on the kinds of choices we’re interested in making, we can now discuss the Java syntax used to describe these choices. It was no accident that we kept repeating the word “if,” because the main Java language feature for making choices is called an if statement.

4.3.1. if statements

The designers of Java studied Boolean logic and created a type called boolean. Every condition used by an if statement must evaluate to a boolean value, which can only be one of two things: true or false.

For example, we could have a boolean variable called raining. Stored in this variable is the value true if it’s raining and false if it isn’t. Using Java syntax, we could encode our first example in which our actor takes her umbrella when it’s raining.

if(raining) {
    umbrella.take();
}

The action taken if it is raining is done by calling a method on an object. We’ll discuss objects and methods further in Chapter 8 and Chapter 9. What we’re focusing on now is that the line umbrella.take(); is executed only if raining has the value true. Nothing is done if it’s false. Figure 4.1 shows this pattern of conditional execution followed by all if statements.

if
Figure 4.1 Execution goes inside the if statement when its condition is true and skips past it otherwise.

Our descriptions of logical scenarios from the previous section used the word “then” to mark the actions that would be done if a condition was true. Some languages use then as a keyword, but Java doesn’t. Instead, note the left brace ({) and the right brace (}) that enclose the executable line umbrella.take();. These braces serve the same role as the word “then,” clearly marking the action to be performed if a condition is true. Braces are unambiguous because they mark a start and an end. If there are many actions to be done, they can all be put inside the braces, and there will be no question as to which actions are associated with a given if statement.

For example, we may also need to close the window and put on a raincoat if it’s raining. We might accomplish these tasks in Java as follows.

if(raining) {
    umbrella.take();
    window.close();
    raincoat.putOn();
}

Within a matching pair of braces ({ }), called a block of code, execution proceeds normally, line by line. First, the JVM will cause the umbrella to be taken, then the window to be closed, and finally the raincoat to be put on.

If only a single line of code is contained within a block of code, the braces can be left out. For example, many experienced Java programmers would have written our first example as follows.

if(raining)
    umbrella.take();

For beginning Java programmers, however, it’s a good idea to use braces even when you don’t need to. Without braces, code can appear to be doing one thing when it’s really doing another.

Since programmers must often choose between two alternatives, Java provides an else statement to specify code that should be run when the condition of the if statement is false.

Let fiftyDollars be a boolean variable that’s true if we have at least $50 and is false otherwise. Now, we can choose between two dining options based on how much money we have.

if(fiftyDollars) {
    lobsterDinner.eat();
}
else {
    fastFood.eat();
}

This Java code matches the logical statements we wrote before. If we have enough money, we’ll eat a lobster dinner; otherwise, we’ll eat fast food. As with an if statement, we use braces to mark a block of code for an else statement, too. Since a single line of code will be executed in each case, the braces are optional here. We could have written code with the same functionality as follows.

if(fiftyDollars)
    lobsterDinner.eat();
else
    fastFood.eat();

Figure 4.2 shows the pattern of conditional execution followed by all if statements that have a matching else statement.

else
Figure 4.2 Execution goes inside the if statement when its condition is true and jumps into the else statement otherwise.
Pitfall: Misleading indentation

Indentation is used to make code more readable, but Java ignores whitespace, meaning that the indentation has no effect on the execution of the code. To demonstrate, let’s assume that our imaginary diner knows he’ll get a stomachache after eating fast food. Thus, he’ll take some Pepto-Bismol after eating it. If you added this action to the code above, which does not contain braces, you might get the following.

if(fiftyDollars)
    lobsterDinner.eat();
else
    fastFood.eat();
    peptoBismol.take();

Although it looks like both fastFood.eat(); and peptoBismol.take(); are within the block of the else statement, only fastFood.eat(); is. The line peptoBismol.take(); is not part of the if-else structure at all and will be executed no matter what. The correct way to program this decision is below.

if(fiftyDollars)
    lobsterDinner.eat();
else {
    fastFood.eat();
    peptoBismol.take();
}

4.3.2. The boolean type and its operations

Recall that Java uses the type boolean for values that can only be true or false. Just like the numerical types double and int, the boolean type has specific operations that can be used to combine them together. By design, these operations correspond exactly to the logical operations we described before. Here’s a table giving the Java operators equivalent to the logical Boolean operations.

Name Math
Symbol
Java
Operator
Description

AND

&&

Returns true if both values are true

OR

||

Returns true if either value is true

XOR

^

Returns true if values are different

NOT

¬

!

Returns the opposite of the value

Using these operators, we can create boolean values and combine them together.

boolean x = true;
boolean y = false;
boolean z = !((x || y) ^ (x && y));

When this code is executed, the value of z will be false. Although it’s perfectly legal to perform boolean operations this way, it’s much more common to combine them “on the fly” inside of the condition of an if statement. Recall the statement from the previous section:

If I’m bored, or it’s late and I can’t sleep,

I’ll watch television.

If we let bored, late, and canSleep be boolean variables whose values indicate if we are bored, if it is late, and if we can sleep, respectively, we can encode this statement in Java like so.

if(bored || (late && !canSleep))
    television.watch();

Combining the || operator with other || operators is both commutative and associative: order and grouping doesn’t matter. Likewise, combining the && operator with other && operators is also commutative and associative. However, once you start mixing || with &&, it’s a good idea to use parentheses for grouping. If, in the above example, bored is true, late is false, and canSleep is true, then the expression bored || (late && !canSleep) will be true. However, with the same values, the expression bored || late && !canSleep will be false.

Now that we’re discussing ordering, note that || and && are short circuit operators. Short circuit means that, if the value of the expression can be determined without evaluating the rest of it, the JVM won’t bother to compute any more of the expression. With || this situation arises because true OR anything else is still true. With && this situations arises because false AND anything else is still false.

if(true || ((late && !canSleep && isTired && isHungry) ||
    (wantsToFindOutWhatHappensNextInHisFavoriteShow ||
    likesTV)))

The condition of this if statement will always evaluate to true, and its body will always be executed. Because Java knows this, it won’t even bother to check any of the conditions after the first || operator. This short circuit evaluation is done at run time and will work if the value of a variable at the beginning of an OR clause is true. It need not be the literal true.

if(false && ((late || !canSleep || isTired || isHungry) &&
    (wantsToFindOutWhatHappensNextInHisFavoriteShow ||
    likesTV)))

The condition of this if statement will always evaluate to false and its body won’t be executed. As before, nothing after the first && will even be checked. If you’re combining literals and boolean values with the || and && operators, it makes no difference that short circuit evaluation occurs. However, if a method call is part of the clauses, your code might miss valuable side-effects. For example, let the boolean variable working be false in the following.

if(working && doSomethingImportant())

In this case, the doSomethingImportant() method must return a boolean value to be a valid statement. Still, if working is false, the doSomethingImportant() method won’t even be called. As soon as the JVM realizes that it’s applying the && operation to a false value (or an || to a true), it’ll give up. In many cases, doing so is fine. In fact, programmers sometimes exploit this feature to allow code in a method like doSomethingImportant() to run only if it’s safe to do so. In this case, if we assume that we always want to run the doSomethingImportant() method (because it does something important) every time the condition of the if statement is evaluated, we need to restructure the code. For example, we can reverse the order of the two terms in the AND clause to achieve this effect. Alternatively, Java provides non-short circuit versions of the || and && operators, namely | and &, if you need to force full evaluation.

You might have been wondering where the majority of boolean values come from. Most computer programs don’t ask the user a long series of true or false questions before spitting out an answer. Most boolean values in Java programs are the result of comparisons, often of numerical data types.

It’s can be useful to compare two numbers to see if one is larger, smaller, or equal to the other. For example, you might have a double variable called pressure that gives the water pressure in a hydraulic system. Perhaps you also have a constant called CRITICAL_PRESSURE that gives the maximum safe pressure for your system. You can compare these values using the > operator.

if(pressure > CRITICAL_PRESSURE)
    emergencyShutdown();

This code allows you to call the appropriate emergency method when pressure is too high. Of course, the > operator is not the only way to compare two values in Java. We list all the relational operators in Chapter 3, but the table below shows them again in a mathematical context.

Table 4.1 Relational operators
Name Math
Symbol
Java
Operator
Description

Equals

=

==

true if the two values are equal

Not Equals

!=

true if the two values are not equal

Less Than

<

<

true if the first value is strictly less than the second

Less Than or Equals

<=

true if the first value is less than or equal to the second

Greater Than

>

>

true if the first value is strictly greater than the second

Greater Than or Equals

>=

true if the first value is greater than or equal to the second

The concepts and mathematical symbols for these operators should be familiar from mathematics. There are a few differences from the mathematical versions of these ideas that are worth pointing out. First, only easy-to-type symbols are used for Java operators. Thus, we need two characters to represent most relational operators in the language. These operators can be used to compare any numerical type with any other numerical type, including char. In the case of mismatched types, such as an int and a double, the lower precision type is automatically cast to the higher precision type. Care should be taken when using the == operator with floating-point types because of rounding errors. For example, the expression 1.0/3.0 == 0.3333333333 always evaluates to false.

The == operator is not the same as the = operator from previous chapters. In Java, the double equal sign == is used to compare two things while the single equal sign = is used to assign one thing to another.

Confusion can also arise because, in the mathematical world, relational symbols are used to make a statement: x < y is an announcement or a discovery that the value contained in x is, in fact, smaller than the value contained in y. In the Java world, the statement x < y is a test whose answer is true if the value contained in x is smaller than the value contained in y and false otherwise. Using these operators means performing a test at a specific point in the code, asking a question about the values that certain variables or literals (or the results of method calls) have at that moment in time. In another sense, using these comparisons is a way to take numerical data and convert it into the language of boolean values. Note that the following statement does not compile in Java.

if(4)
    x = y + z;

To be used in an if statement, the value 4 must be first compared with some other numerical type to yield a true or false.

Pitfall: Assignment instead of equality

A common pitfall is to forget one of the equal signs in the comparison operator.

if(x = 4)
    x = y + z;

Again, this code won’t compile. If it did, the variable x would be assigned the value 4, which would in turn be given to the if statement, but an if statement doesn’t know what to do with anything other than a boolean value.

Extreme care should be taken when comparing two boolean values. For example, we might have two boolean values likesDogs1 and likesDogs2, corresponding to whether or not two different people like dogs. Let’s say that the value of each one is true if the person likes dogs and false otherwise. We could create an if statement that would work only if their values are the same.

if(likesDogs1 == likesDogs2)
    makeRoommates();

This code correctly calls the makeRoommates() method only if the two individuals feel the same way about dogs. It doesn’t matter if they both like or dislike dogs, but one roommate loving dogs and the other hating dogs will lead to trouble. However, a tiny mistake in the code could yield the following.

if(likesDogs1 = likesDogs2)
    makeRoommates();

In this case, likesDogs1 would be assigned to whatever likesDogs2 is. Then, that value would be given to the if statement. Here, the makeRoommates() method will be called only if likesDogs2 is true, meaning that the second person likes dogs. Thus, the two people will become roommates if the second one likes dogs, and the feelings of the first person won’t be considered. Unlike the x = 4 example, this code will compile with no warning.

The next few examples illustrate the use of the if statement. They also use some methods from class Math.

Example 4.1 Leap year

In the standard Gregorian calendar, leap years occur roughly once every four years. During leap years, the month of February has 29 days instead of 28. This extra day makes up for the fact that it takes a little more than 365.24 days for the earth to orbit the sun. Unfortunately, the orbit of the earth around the sun doesn’t match up in any exact way with the rotation of the earth, making exceptions to the rule of every four years.

In fact, the official definition for a leap year is a year that is evenly divisible by 4, except for those years that are evenly divisible by 100, with the exception to the exception of years that are evenly divisible by 400. For example, 1988 was a leap year because it was divisible by 4. The year 1900 was not a leap year because it was divisible by 100 but not by 400, and the year 2000 was a leap year because it was divisible by 400.

Recall that the mod operator (%) allows us to find the remainder after integer division. Thus, if n % 100 gives zero, n has no remainder after being divided by 100 and must be evenly divisible by 100.

Program 4.1 Prompts the user for a year and then determines whether or not it is a leap year.
import java.util.*;

public class LeapYear {
    public static void main(String[] args) {                
        Scanner in = new Scanner( System.in );      
        System.out.print("Please enter a year: ");
        int year = in.nextInt();
        if( year % 400 == 0 )
            System.out.println(year + " is a leap year.");
        else if( year % 100 == 0 )
            System.out.println(year + " is not a leap year.");
        else if( year % 4 == 0 )
            System.out.println(year + " is a leap year.");
        else
            System.out.println(year + " is not a leap year.");
    }
}

As with all of the programs in this section, we begin by importing java.util.*, which is needed for the Scanner class for input. The program prompts the user for a year and reads it in. If the year is evenly divisible by 400, the program outputs that it’s a leap year. Otherwise, if the year is evenly divisible by 100, the program outputs that it is not a leap year. Otherwise, if the year is evenly divisible by 4, the program outputs that it’s a leap year. Finally, if all the other conditions have failed, the program outputs that the year is not a leap year.

Example 4.2 Quadratic formula

The quadratic formula is a useful tool from mathematics. Using this formula, you can solve equations of the form ax2 + bx + c = 0. You might recall the statement of the quadratic formula given below.

quadratic

The b2 - 4ac part of the formula is called the discriminant. If the discriminant is positive, there will be two real answers to the equation. If the discriminant is negative, there will be two complex answers to the equation. Finally, if the discriminant is zero, there will be a single real answer to the problem. If you want to write a program to solve quadratic equations for you, it should take these three possibilities into account.

Program 4.2 Solves a quadratic equation.
import java.util.*;

public class Quadratic {
    public static void main(String[] args) {                
        Scanner in = new Scanner(System.in);
        System.out.println("This program solves quadratic" + 
            " equations of the form ax^2 + bx + c = 0.");
        System.out.print("Please enter a value for a: "); (1)
        double a = in.nextDouble();
        System.out.print("Please enter a value for b: ");
        double b = in.nextDouble();
        System.out.print("Please enter a value for c: ");
        double c = in.nextDouble();
        double discriminant = b*b - 4*a*c; (2)
        if(discriminant == 0.0) (3)
            System.out.println("The answer is x = " + (-b/(2*a)));
        else if(discriminant < 0.0) (4)
            System.out.println("The answers are x = " + (-b / (2*a)) + " + "
				+ Math.sqrt(-discriminant) / (2*a) + "i and x = "
				+ (-b / (2*a)) + " - " + Math.sqrt(-discriminant) / (2*a) + "i");
        else (5)
            System.out.println("The answers are x = " + (-b + Math.sqrt(discriminant))/(2*a)
				+ " and x = " + (-b - Math.sqrt(discriminant))/(2*a));
    }
}
1 This program prompts the user and reads in values for a, b, and c.
2 Then, it computes the discriminant.
3 In the first case, we test to see if the discriminant is zero and print the single answer.
4 Next, if the discriminant is negative, we compute the real and complex parts separately and output the two answers.
5 Finally, if the discriminant is positive, we find the two real answers and output them.

Note that braces were not needed for the if, else-if, and else blocks because each is composed of only a single line of code. Although these System.out.println() method calls may take up more than one line visually, Java interprets them as single lines because they each only have a single semicolon (;).

The line if(discriminant == 0.0) is dangerous since we’re using double values. Because of rounding errors, the discriminant might not be exactly zero even if it should be, mathematically. Industrial strength code would probably check to see if the absolute value of the discriminant is less than a very small number (such as 0.00000001). Values that small would then be treated as if they were zero.

Example 4.3 20 Questions

In the time-honored game of 20 Questions, one person mentally chooses something, and the other participants must guess what the thing is by asking questions whose answer is either “yes” or “no.” In one popular version, the person who chooses the thing starts by declaring whether it is animal, vegetable, or mineral.

Using counting principles from math, 20 yes-or-no questions makes it possible to differentiate 220 = 1,048,576 items. If you’re also told whether the thing is animal, vegetable, or mineral, it should be possible to guess over 3 million items! At this point in our development as Java programmers, we’re not yet ready to deal with such a large range of possibilities. To keep the size of the code reasonable, let’s narrow the field to 10 different items: a lizard, an eagle, a dolphin, a human, some lead, a diamond, a tomato, a peach, a maple tree, and a potato.

flowchart
Figure 4.3 Decision tree to distinguish 10 items.

Using these items, we can construct a tree of decisions, starting with the decision between animal, vegetable, and mineral. If the thing is an animal, we could then ask if it is a mammal. If it is a mammal, we could ask if it lives on land, deciding between human and dolphin. If it’s not a mammal, we could ask if it flies, deciding between an eagle and a lizard. We can construct similar questions for the things in the vegetable and mineral categories, matching Figure 4.3.

Program 4.3 Navigates the possible choices in the decision tree.
import java.util.*;

public class TwentyQuestions {
    public static void main(String[] args) {                
        Scanner in = new Scanner(System.in);      
        System.out.print("Is it an animal, vegetable, or mineral? (a, v, or m): ");
        String response = in.next().toLowerCase();
        if(response.equals("a")) {
            System.out.print("Is it a mammal? (y or n): ");
            response = in.next().toLowerCase();
            if(response.equals("y")) {
                System.out.print(
                    "Does it live on land? (y or n): ");
                response = in.next().toLowerCase();
                if(response.equals("y"))
                    System.out.println("It's a human.");                
                else // Assume "n"
                    System.out.println("It's a dolphin.");              
            }
            else { // Assume "n"
                System.out.print("Does it fly? (y or n): ");
                response = in.next().toLowerCase();
                if(response.equals("y"))
                    System.out.println("It's an eagle.");               
                else // Assume "n"
                    System.out.println("It's a lizard.");
            }
        }
        else if(response.equals("v")) {
            System.out.print("Is it a fruit? (y or n): ");
            response = in.next().toLowerCase();
            if(response.equals("y")) {
                System.out.print(
                    "Does it grown on a vine? (y or n): ");
                response = in.next().toLowerCase();
                if(response.equals("y"))
                    System.out.println("It's a tomato.");               
                else // Assume "n"
                    System.out.println("It's a peach.");                
            }
            else { // Assume "n"
                System.out.print("Is it a tree? (y or n): ");
                response = in.next().toLowerCase();
                if(response.equals("y"))
                    System.out.println("It's a maple tree.");
                else // Assume "n"
                    System.out.println("It's a potato.");
            }
        }
        else { // Assume "m"
                System.out.print(
                    "Is it the hardest mineral? (y or n): ");
                response = in.next().toLowerCase();
                if(response.equals("y"))
                    System.out.println("It's a diamond.");              
                else // Assume "n"
                    System.out.println("It's lead.");
        }       
    }
}

The code in this example is straightforward, although even 10 items makes for a lot of if and else blocks. Other than the if-else statements, only simple input and output are needed to make the program function. For proper String comparison, it’s necessary to use the equals() method to test if two String values are the same.

Note that we’ve added comments specifying what we assume is the case for each else block. If we were being more careful, we should test for the "y" and "n" cases and then give an error message when the user inputs something unexpected, like "x" or "149" or even "no". Again, note that no braces are needed for the final if-else blocks in which the guess is made, since each of these guesses requires only a single line of code.

You might be curious how to make a real 20 Questions game that could learn over time. To do so, many more programming tools are necessary: repetition, data structures (so that you can organize the questions), and file input and output (so that you can store new information permanently). These concepts are covered in later chapters.

4.3.3. switch statements

The if statement is the workhorse of Java conditional execution. With enough care, you can craft code that can make any fixed sequence of decisions with arbitrary complexity. Even so, the if statement can be a little clumsy because it only allows you to choose between two alternatives. After all, a conditional can only be true or false. Certainly, decisions can be nested, allowing for more than two possibilities, but long lists of possibilities can be cumbersome and hard to read.

For example, imagine that we want to create a program that determines the appropriate gift for a wedding anniversary. Below is a table of traditional categories of gifts based on the anniversary year.

Year Gift Year Gift

1

Paper

13

Lace

2

Cotton

14

Ivory

3

Leather

15

Crystal

4

Fruit

20

China

5

Wood

25

Silver

6

Candy / Iron

30

Pearl

7

Wool / Copper

35

Coral

8

Bronze / Pottery

40

Ruby

9

Pottery / Willow

45

Sapphire

10

Tin / Aluminum

50

Gold

11

Steel

55

Emerald

12

Silk / Linen

60

Diamond

Let year be a variable of type int containing the year in question. A structure of if-else statements that can determine the appropriate gift based on the year is below.

String gift;
if(year == 1)
    gift = "Paper";
else if(year == 2)
    gift = "Cotton";
else if(year == 3)
    gift = "Leather";
else if(year == 4)
    gift = "Fruit";
else if(year == 5)
    gift = "Wood";
else if(year == 6)
    gift = "Candy / Iron";
else if(year == 7)
    gift = "Wool / Copper";
else if(year == 8)
    gift = "Bronze / Pottery";
else if(year == 9)
    gift = "Pottery / Willow";
else if(year == 10)
    gift = "Tin / Aluminum";
else if(year == 11)
    gift = "Steel";
else if(year == 12)
    gift = "Silk / Linen";
else if(year == 13)
    gift = "Lace";
else if(year == 14)
    gift = "Ivory";
else if(year == 15)
    gift = "Crystal";
else if(year == 20)
    gift = "China";
else if(year == 25)
    gift = "Silver";
else if(year == 30)
    gift = "Pearl";
else if(year == 35)
    gift = "Coral";
else if(year == 40)
    gift = "Ruby";
else if(year == 45)
    gift = "Sapphire";
else if(year == 50)
    gift = "Gold";
else if(year == 55)
    gift = "Emerald";
else if(year == 60)
    gift = "Diamond";
else
    gift = "No traditional gift";

This code stores the correct value in gift. Note that we are using the feature of if statements that treats an entire if statement as one statement. If we used braces to group things properly, the code would become unreadable and unmanageably large.

String gift;
if(year == 1) {
    gift = "Paper";
}
else {
    if(year == 2) {
        gift = "Cotton";
    }
    else {
        if(year == 3) {
            gift = "Leather";
        }
        else {
            if(year == 4) {
                gift = "Fruit";
            }
            .
            .
            .

It appears that there’s some kind of else if construct in Java, but there isn’t. Still, careful use of the rules for braces allows us to write code that nicely expresses a sequence of alternatives.

Another way of expressing a long sequence of alternatives is by using a switch statement. A switch statement takes a single integer type value (int, long, short, byte, char) or a String and jumps to a case corresponding to the input. We can recode the anniversary gift example using a switch statement as follows.

String gift;
switch(year) {
    case 1:  gift = "Paper"; break;
    case 2:  gift = "Cotton"; break;
    case 3:  gift = "Leather"; break;
    case 4:  gift = "Fruit"; break;
    case 5:  gift = "Wood"; break;
    case 6:  gift = "Candy / Iron"; break;
    case 7:  gift = "Wool / Copper"; break;
    case 8:  gift = "Bronze / Pottery"; break;
    case 9:  gift = "Pottery / Willow"; break;
    case 10: gift = "Tin / Aluminum"; break;
    case 11: gift = "Steel"; break;
    case 12: gift = "Silk / Linen"; break;
    case 13: gift = "Lace"; break;
    case 14: gift = "Ivory"; break;
    case 15: gift = "Crystal"; break;
    case 20: gift = "China"; break;
    case 25: gift = "Silver"; break;
    case 30: gift = "Pearl"; break;
    case 35: gift = "Coral"; break;
    case 40: gift = "Ruby"; break;
    case 45: gift = "Sapphire"; break;
    case 50: gift = "Gold"; break;
    case 55: gift = "Emerald"; break;
    case 60: gift = "Diamond"; break;
    default: gift = "No traditional gift"; break;
}

Just like an if statement, a switch statement always has parentheses enclosing some argument. Unlike an if, the argument of a switch must be some kind of data that can be expressed as an integer or a String, not a boolean. For each of the possible values you want the switch to handle, you write a case statement. A case statement consists of the keyword case followed by a constant value, either a literal or a named constant, then a colon. When executed, the JVM jumps to the matching case label and starts executing code there. If there is no matching case label, the JVM goes to the default label. If there is no default label, the entire switch statement is skipped.

One unusual feature of switch statements is that execution falls through case statements. This means that you can use many different case statements for a single segment of executable code. The execution of code in a switch statement jumps out when it hits a break statement. However, break statements are not required, as shown in this switch statement that gives location information for all of the telephone area codes in New York state.

String location = "";
switch(code) {
    case 917: location = "Cellular: ";
    case 212:
    case 347:
    case 646:
    case 718: location += "New York City"; break;

    case 315: location = "Syracuse"; break;

    case 516: location = "Nassau County"; break;

    case 518: location = "Albany"; break;

    case 585: location = "Rochester"; break;

    case 607: location = "South Central New York"; break;

    case 631: location = "Suffolk County"; break;

    case 716: location = "Buffalo"; break;

    case 845: location = "Lower Hudson Valley"; break;

    case 914: location = "Westchester County"; break;

    default:  location = "Unknown Area Code"; break;
}

As you can see, five different area codes are used by New York City. By leaving out the break statements, values of 212, 347, 646, and 718 all have "New York City" stored into location. Area code 917 was originally designated for cellular phones and pagers although now it includes some landlines. By cleverly putting the statement for 917 ahead of the other New York City entries, a value of 917 first stores "Cellular: " into location and then falls through and appends "New York City". For each of these five area codes, execution in the switch statement ends only when the break statement is reached.

The remaining nine area codes are separate. Each of them does a single assignment and then breaks out of the switch block. Finally, the default label is used if the area code doesn’t match one of the given codes. Note that we’ve ordered the (non-NYC) area codes in ascending order for the sake of readability. As shown in the 917 example, there’s no rule about the ordering of the labels. Even the default label can occur anywhere in the switch block you want, although it’s common to put it at the end. Also, the break after the default label is unnecessary because execution exits the switch block anyway. Nevertheless, it’s always wise to end on a break, in the event that you add more cases in later.

Carelessness is always something to watch out for in switch statements. Leaving out a break statement can cause disastrous and difficult to discover bugs. The compiler does not warn you about missing break statements, either. It’s entirely your responsibility to use them appropriately. Because of the dangers involved, it’s often safe to use if-else statements. Any switch statement can be rewritten as some combination of if-else statements, but the reverse is not true. The benefit of switch statements is their ability to list many alternatives clearly. Their drawbacks include the ease of making a mistake, an inability to express ranges of data or most types (double, float, or any reference type other than String), and limited expressive power. They should be used only when their benefit of clearly displaying a list of data outweighs the drawbacks.

Next we give a number of examples to help you get more familiar with switch statements.

Example 4.4 Days in the month

There are fewer uses for switch statements than if statements. Nevertheless, there are problems where their fall-through behavior can be useful. Imagine that you need to write a program that gives the length of each month (with the assumption that February always has 28 days). Given the month as a number, we can use switch statements to write a program that maps the number of the month to the number of days it contains.

Program 4.4 Computes the number of days in a given month.
import java.util.*;

public class DaysInMonth {
    public static void main(String[] args) {                
        Scanner in = new Scanner( System.in );      
        System.out.print("Please a month number (1-12): ");
        int month = in.nextInt();
        int days = 0;
        switch( month ) {
            case 2:  days = 28; break; (1)

            case 4:
            case 6:
            case 9:
            case 11: days = 30; break; (2)

            case 1:
            case 3:
            case 5:
            case 7:
            case 8:
            case 10:
            case 12: days = 31; break; (3)
        }   
        System.out.println("The month you entered has " + days + " days.");
    }
}
1 This program has a single label for February setting days to 28.
2 Then, there are labels for April, June, September, and November, months that each have 30 days.
3 Finally, the large block of January, March, May, July, August, October, and December all set days to 31.

It would be easy to extend this code to prompt the user for a year so that you could integrate the leap year code from above for the February case. Note also that we didn’t use a default label. You might want to set days to some special value (like -1) for invalid months.

In this program it’s necessary to initialize days to some value, in this case 0. Otherwise, the program won’t compile, since it’ll try to print out the value inside days, a value that won’t exist if month is not in the range 1 through 12.

Example 4.5 Ordinal numbers

The term ordinal numbers refers to numbers that are used for ordering within a set of items: first, second, third, and so on. When writing these numbers with numerals in English, it is common to append two letters to the end of the numeral to give the reader a clue that these numerals should be read with their ordinal names: 1st, 2nd, 3rd, and so on.

Unlike most things in English, the rules for deciding which two letters are relatively simple. If the number ends in a 1, the letters “st” should generally be used. If the number ends in a 2, the letters “nd” should generally be used. If the number ends in a 3, the letters “rd” should generally be used. For most other numbers, the letters “th” should be used. We can use a switch statement to write a program to give the correct ordinal endings for most numbers as follows.

Program 4.5 Appends the appropriate suffix to a numeral to make it an ordinal.
import java.util.*;

public class Ordinals {
    public static void main(String[] args) {                
        Scanner in = new Scanner( System.in );      
        System.out.print("Please enter a positive number: ");
        int number = in.nextInt();
        String ending;
        switch( number % 10 ) {
            case 1:  ending = "st"; break;
            case 2:  ending = "nd"; break;
            case 3:  ending = "rd"; break;
            default: ending = "th"; break;
        }   
        System.out.println("Its ordinal version is "
            + number + ending + ".");
    }
}

This program prompts and then reads in an int from the user. We then find the remainder of number when it’s divided by 10, yielding its last digit. Based on this digit, we can pick from the four possibilities and output the correct ordinal number in most cases. Unfortunately, the names for English numbers have an inconsistent naming convention between 11 and 19, inclusive, and the ordinals for any number ending in 11, 12, or 13 will be given the wrong suffix by our code. We leave a more complete solution as an exercise.

Example 4.6 Astrology

Many cultures practice astrology, a tradition that the time of a person’s birth impacts his or her personality or future. One important element of Chinese astrology is their zodiac, consisting of 12 animals. Each consecutive year in a 12-year cycle corresponds to an animal. Because this system repeats, the year one is born in modulo 12 identifies the animal. Below is a table giving these values. For example, if you were born in 1979, 1979 mod 12 ≡ 11; thus, you would be a Ram. Note that this arrangement is based on years in the Gregorian calendar. Chinese astrologers do not list the Monkey as the first animal in the cycle. Note that the names of these animals are also sometimes translated in slightly different ways.

Animal Year
modulo 12
Animal Year
modulo 12

Monkey

0

Tiger

6

Rooster

1

Rabbit

7

Dog

2

Dragon

8

Boar

3

Snake

9

Rat

4

Horse

10

Ox

5

Ram

11

Unfortunately, this table is not very accurate because it’s based on numbering from the Gregorian calendar. The years in question actually start and end based on Chinese New Year, which occurs between January 21 and February 20. As a consequence, you may miscalculate your animal if your birthday is early in the year. Since calculating the date of Chinese New Year is challenging, let’s ignore this problem for the moment and write a program using a switch statement designed to correctly output the animal corresponding to an input birth year.

Program 4.6 Determines a Chinese zodiac animal based on birth year.
import java.util.*;

public class ChineseZodiac {
    public static void main(String[] args) {                
        Scanner in = new Scanner( System.in );      
        System.out.print("Please enter a year: ");
        int year = in.nextInt();        
        String animal = "";
        switch( year % 12 ) {
            case 0:  animal = "Monkey"; break;
            case 1:  animal = "Rooster"; break;
            case 2:  animal = "Dog"; break;
            case 3:  animal = "Boar"; break;
            case 4:  animal = "Rat"; break;
            case 5:  animal = "Ox"; break;
            case 6:  animal = "Tiger"; break;
            case 7:  animal = "Rabbit"; break;
            case 8:  animal = "Dragon"; break;
            case 9:  animal = "Snake"; break;
            case 10: animal = "Horse"; break;
            case 11: animal = "Ram"; break;
        }   
        System.out.println("The Chinese zodiac animal for this year is: " + animal);
    }
}
Sign Symbol Date Range

Aries

The Ram

March 21 to April 19

Taurus

The Bull

April 20 to May 20

Gemini

The Twins

May 21 to June 20

Cancer

The Crab

June 21 to July 22

Leo

The Lion

July 23 to August 22

Virgo

The Virgin

August 23 to September 22

Libra

The Scales

September 23 to October 22

Scorpio

The Scorpion

October 23 to November 21

Sagittarius

The Archer

November 22 to December 21

Capricorn

The Sea-Goat

December 22 to January 19

Aquarius

The Water Bearer

January 20 to February 19

Pisces

The Fishes

February 20 to March 20

In Western astrology, an important element associated with a person’s birth is also called a zodiac sign. The dates for determining this kind of zodiac sign are given by the preceding table.

If you want to implement the rules for this zodiac in code, a switch statement is a good place to start, but you also have to put if statements for each month to test the exact range of dates.

Program 4.7 Determines Western zodiac signs based on birth month and day.
import java.util.*;

public class WesternZodiac {
    public static void main(String[] args) {                
        Scanner in = new Scanner(System.in);      
        System.out.print("Please enter a month number (1-12): ");
        int month = in.nextInt();
        System.out.print("Please enter a day number in that month (1-31): ");
        int day = in.nextInt();
        String sign = "";
        switch(month) {
            case 1: if(day < 20)
                    sign = "Capricorn";
                else
                    sign = "Aquarius";
                break;
            case 2: if(day < 20)
                    sign = "Aquarius";
                else
                    sign = "Pices";
                break;
            case 3: if(day < 20)
                    sign = "Pices";
                else
                    sign = "Aries";
                break;
            case 4: if(day < 20)
                    sign = "Aries";
                else
                    sign = "Taurus";
                break;
            case 5: if(day < 21)
                    sign = "Taurus";
                else
                    sign = "Gemini";
                break;
            case 6: if(day < 21)
                    sign = "Gemini";
                else
                    sign = "Cancer";
                break;
            case 7: if(day < 23)
                    sign = "Cancer";
                else
                    sign = "Leo";
                break;
            case 8: if(day < 23)
                    sign = "Leo";
                else
                    sign = "Virgo";
                break;
            case 9: if(day < 23)
                    sign = "Virgo";
                else
                    sign = "Libra";
                break;
            case 10:if(day < 23)
                    sign = "Libra";
                else
                    sign = "Scorpio";
                break;
            case 11:if(day < 22)
                    sign = "Scorpio";
                else
                    sign = "Sagittarius";
                break;
            case 12:if(day < 20)
                    sign = "Sagittarius";
                else
                    sign = "Capricorn";
                break;          
        }   
        System.out.println("The zodiac sign is: " + sign);
    }
}

This program is just slightly more complex than the program for the Chinese zodiac. You still need to jump to 12 different cases (numbered 1-12 instead of 0-11), but additional day information is needed to pin down the sign.

4.4. Solution: Monty Hall

We now return to the Monty Hall simulation described at the beginning of the chapter. Recall that objects of the Random class allow us to generate all kinds of random values. To implement this simulation successfully, our program must make the decisions needed to set up the game for the user as well as respond to the user’s input.

import java.util.*; (1)

public class MontyHall {
    public static void main(String[] args) {
        Random random = new Random();
        int winner = random.nextInt(3); (2)
        Scanner in = new Scanner( System.in );
        System.out.print("Choose a door (enter 0, 1, or 2): ");
        int choice = in.nextInt(); (3)
        int alternative; (4)
        int open;
1 We begin with the import statement needed to use both the Scanner and Random class and then define the MontyHall class.
2 In the main() method we first decide which of the three doors is the winner. To do so, we instantiate a Random object and use it to generate a random number that is either 0, 1, or 2 by calling the nextInt() method with an argument of 3. We could have added 1 to this value to get a random choice of 1, 2, or 3, but many counting systems in computer science start with 0 instead of 1. We might as well embrace it.
3 Next, we prompt the user to pick from the three doors and read the choice.
4 Finally, we declare two more int values to keep track of which door to open and which door is the alternative that the user can choose to change over to.

Now, we have to navigate a complicated series of decisions.

        if( choice == winner ) { (1)
            int low;
            int high;
            if( choice ==  0 ) { (2)
                low = 1;
                high = 2;
            }
            else if( choice == 1 ) {
                low = 0;
                high = 2;
            }
            else { //choice == 2
                low = 0;
                high = 1;
            }   
            //randomly choose between other two doors
            double threshold = random.nextDouble(); (3)
            if( threshold < 0.5 ) { (4)
                alternative = low;
                open = high;
            }
            else { (5)
                alternative = high;
                open = low;
            }           
        }
1 In this segment of code, we tackle the possibility that the user happened to choose the winning door. To obey the rules of the game, we must randomly pick which of the two other doors to open.
2 First, we determine which are the other two doors and save them in low and high, respectively.
3 Then, we generate a random number.
4 If the random number is less than 0.5, we keep the lower numbered door as an alternative choice for the user and open the higher numbered door.
5 If the random number is greater than or equal to 0.5, we do the opposite.
        else { (1)
            alternative = winner;
            if( choice == 0 ) { (2)
                if( winner == 1 )                 
                    open = 2;               
                else
                    open = 1;               
            }
            else if( choice == 1 ) {
                if( winner == 0 )                   
                    open = 2;               
                else
                    open = 0;               
            }
            else { //choice == 2 
                if( winner == 0 )                       
                    open = 1;               
                else
                    open = 0;               
            }           
        }
1 This else block covers the case that the player did not pick the winning door the first time. Unlike the previous code segment, we no longer have a choice of which door to open.
2 Instead, we must always make the winner the alternative for the user to pick. Then, we simply determine which door is left over so that we can open it.

Note that the braces surrounding the blocks for each of the braces surrounding the blocks for each of the three possible values of choice are not necessary but are included for readability.

        System.out.println("We have opened Door " + open + (1)
            ", and there is junk behind it!");
        System.out.print("Do you want to change to Door " + (2)
            alternative + " from Door " + choice +
            "? (Enter 'y' or 'n'): ");
        String change = in.next();      
        if( change.equals("y") )
            choice = alternative;
        System.out.println("You chose Door " + choice);
        if( choice == winner ) (3)
            System.out.println("You win a pile of gold!");
        else
            System.out.println("You win a pile of junk.");
    }
}
1 This final segment of code informs the user which door has been opened.
2 It prompts the user to change his or her decision.
3 Depending on the final choice, the program says whether or not the user wins gold or junk.

4.5. Concurrency: Selection

Selection statements (if and switch) seem to have little to do with concurrency or parallelism. Selection allows you to choose between alternatives while concurrency is about the interaction between different threads of execution. As it turns out, there are two reasons why selection and concurrency are deeply related to each other.

The first reason is that selection is one of the most basic tools in Java. It’s impossible to go more than a few lines of a code without encountering a selection statement, usually an if. Concurrent programs are not exempt from this dependence on if statements. Making decisions is at the heart of all programming languages running on all computers.

The second, more troubling reason is related to a problem with some concurrent programs called a race condition, which is discussed in detail in Chapter 14. Remember, one of the biggest challenges of programming a computer is thinking in a completely sequential and logical way. Each line of code is executed one after the other. Adding in if statements means that some code is executed only if a condition is true and skipped otherwise. Consider the following fragment of code:

if(!matches.areLit() && !flyingSparks) {
    storageRoom.enter();
    dynamite.unpack();
}

In this if statement, an imaginary agent only enters the storage room and unpacks the dynamite if the matches are not lit and there are no flying sparks. When execution reaches the first line inside the if block, we are certain that matches.areLit() returned false and flyingSparks is false. This is a one-time check. If the first thing that happens inside the if block is code that lights the matches, Java will not jump out of the if statement.

As always, the programmer is responsible for making an if statement that makes sense. It’s possible that entering the storage room or unpacking the dynamite causes sparks to fly or matches to burst into flames spontaneously, but it seems unlikely. If the storageRoom and dynamite objects were written by other people, we would expect their documentation to explain unusual side-effects of this kind. In a sequential program, the programmer can be reasonably sure that it’s safe to unpack the dynamite.

Consider another fragment of code:

matches.light();
flyingSparks = sparklers.light(matches);

This code appears to light the matches and then to use the lit matches to set some sparklers on fire. Presumably, if the process was successful, flyingSparks will have the value true. This code is reasonable and potentially helpful. If you were celebrating the 4th of July or needed to signal a passing helicopter to rescue you from a desert island, lighting sparklers could be a great idea. This sparkler-lighting code could occur before the dynamite-unpacking code or after it, but, in a sequential program, the protection of the if statement keeps our hero from being blown up if she tries to unpack the dynamite with lit sparklers.

In a concurrent program, all bets are off. Another thread of execution can be operating at the very same time. It’s as if our hero is trying to unpack the dynamite while the villain is lighting sparklers and tossing them into the storage room. If the thread of execution gets to the if statement and makes sure that the matches aren’t lit and that there are no flying sparks, it continues onward. If sparks start flying after that check, it still continues onward, oblivious of the fact. Even though this risk of explosion exists, it depends on the timing of the two (or more) concurrent threads of execution. It might be possible to run a program 1,000 times with no problem. But if the timing is wrong on the 1,001st time, BOOM!

At this point, you don’t need to worry about values inside your if statements being changed by other segments of code, but that problem is at the heart of why concurrent programming can be so difficult. Whether or not you’re programming concurrently, it’s important to keep in mind the assumptions your code makes and the way different parts of your program interact with each other.

4.6. Exercises

Conceptual Problems

  1. Given that x, y, and z are propositions in Boolean logic, make a truth table for the expression ¬(x ∧ ¬y) ⊕ ¬z.

  2. What’s the value of the Boolean expression ¬( (T ⊕ F) ∧ ¬(F ∨ T) )?

  3. The calculation to determine the leap year given in Example 4.1 uses three if statements and three else statements. Write the leap year calculation using a single if and a single else. Feel free to use boolean connectors such as || and &&.

  4. The XOR operator (^) is useful for combining boolean values, but it can be replaced with a more commonly used relational operator in Java. Which one?

  5. De Morgan’s laws are the following, which show that the process of negating a clause changes an AND to an OR and vice versa.

    ¬(xy) = ¬x ∨ ¬y
    ¬(xy) = ¬x ∧ ¬y

    Create truth tables to verify both of these statements.

  6. Use De Morgan’s laws given above to rewrite the following statement in Java to an equivalent statement that contains no negations.

    boolean value = !((x != 4) && (y < 2));
  7. Consider the following fragment of code.

    int x = 5;
    int y = 3;
    if(y > 10 && (x = 10) > 5)
        y++;
    System.out.println("x: " + x);
    System.out.println("y: " + y);

    What’s the output? Is the output changed if the condition of the if statement is changed to y > 10 & (x = 10) > 5? Why?

  8. Consider the following fragment of code.

    int a = 7;
    if(a++ == 7)
        System.out.println("Seven");
    else
        System.out.println("Not seven");

    What’s the output? Is the output changed if the condition of the if statement is changed to ++a == 7? Why?
    Note: It is generally wise to avoid increment, decrement, and assignment statements in the condition of an if statement because of the confusion that can arise.

Programming Practice

  1. Write programs that:

    1. Read in two double values and print the larger of the two of them.

    2. Read in three double values and print the largest of the three out. Note: You should use nested if statements.

  2. Write programs that:

    1. Read an int value from the user specifying a certain number of cents. Use if statements to print out the name of the corresponding coin in U.S. currency according to the table below. If the value doesn’t match any coin, print no coin.

      Cents Coin

      1

      penny

      5

      nickel

      10

      dime

      25

      quarter

      50

      half-dollar

      100

      dollar

    2. Read a String value from the user that gives one of the 6 coin names given in the table above. Use if statements to print out the corresponding number of cents for the input. If the name doesn’t many any coin, print unknown coin.

  3. Re-implement both parts from Exercise 4.10 using switch statements instead of if statements.

  4. Expand the program given in Example 4.5 to give the correct suffixes (always “th”) for numbers that end in 11, 12, and 13. Use the modulus operator to find the last two digits of the number. Using an if statement, a switch statement, or a combination, check for those three cases before going into the normal cases.

  5. At the bottom of Section 4.3.3, we use a switch statement to determine the location of various area codes in New York state. Write an equivalent fragment of code using if-else statements instead.

  6. Every member of your secret club has an ID number. These ID numbers are between 1 and 1,000,000 and have two special characteristics: They are multiples of 7 and all end with a 3 in the one’s place. For example, 63 is the smallest such value, and 999,943 is the largest such value. Write a program that prompts the user for an int value, reads it in, and then says whether or not it could be used as an ID number. Note: You need to use the modulus operator in two different ways to test the value correctly.

  7. According to the North American Numbering Plan (NANP) used by the United States, Canada, and a number of smaller countries, a legal telephone number takes the form XYY-XYY-YYYY, where X is any digit 2-9 and Y is any digit 0-9. Write a program that reads in a String from the user and verifies that it is a legal NANP phone number. The length of the entire String must be 12. The fourth and eight characters in the String (with indexes 3 and 7) must be hyphens (-), and all the remaining digits must be in the correct range. Use the charAt() method of the String class to get the char value at each index. Note: There are several ways to structure the if statements you need to use, but the number of conditions may become large. (23 or more!)

  8. Re-implement the solution to the Monty Hall program given in Section 4.4 using JOptionPane to generate GUIs for input and output.