Decisions and Logic

Comparisons

Comparing ints

int x, y, z;

x < y
x > y
x <= y
x >= y
x == y
x != y

WARNING: Don't confuse x = y and x == y!!!!!

Comparing floats

double x, y, z;

x < y
x > y
x <= y
x >= y
x == y
x != y

Beware of x == y:

x == Math.sqrt(x * x); // returns false!

Use this instead:

class MyUtils {
   private static final double DELTA = 1e-10;
   public static boolean close(double x, double y) {
      return Math.abs(x – y) <= DELTA;
   }
   // etc.
}

Comparing Strings

Use == for literal equality and equals for logical equality.

String s1 = "Hello", s2 = "Hello", s3 = s1;

s1 == s2; // = false
s1 == s3; // = true
s1.equals(s2); // = true
s2.equals(s3); // = true

String s4 = "bye";

s1.compareTo(s4) < 0 // = false

Comparing Objects, null

Rectangle r1(2, 3, 5, 6), r2(2, 3, 5, 6), r3 = r1, r4

r1 == r2 // = false
r1 == r3 // = true
r1.equals(r2) // = true
r4 == null // = true

Boolean Logic

boolean a = true, b = false, c = true, d;

a && b && c // = false
a || b || c // = true
!(a && b) // = true
!(a && b) = !a || !b // DeMorgan's Law

Selections

Selections, also called conditionals, allow programmers to specify that a command should be executed only if a certain condition is true. Of course the truth of the condition generally won't be known until runtime. Selections are classified as one-way, two-way, and multi-way.

One-Way Selections

The form of a one-way selection is:

if (EXPRESSION) COMMAND;

Semantically, the expression, called the condition, is evaluated. If the value of this expression is true, then the command is executed, otherwise, control falls through to the next command. We often use fragments of flowcharts to describe the semantics of control commands:

One-way selections are typically used to terminate programs and functions when error conditions arise. For example, the following command might be used early in main to terminate the program if the value of x isn't positive:

if (x <= 0) return 1; // STOP, x must be positive!
// control only reaches this point if 0 < x

Two-Way Selections

The syntax of a two-way selection is

if (EXPRESSION) COMMAND; else COMMAND;

Once again, the expression is called the condition. The first command is called the consequence, and the second command is called the alternative:

if (CONDITION) CONSEQUENCE; else ALTERNATIVE;

Semantically, the condition is evaluated. If its value is true, then the consequence is evaluated and the alternative is ignored. Otherwise, the alternative is evaluated and the consequence is ignored. Here's the flowchart representation:

In the following example, a two-way selection prevents the user from dividing by zero:

if (den) {
   result = 1/den;
} else {
   System.err.println("can't divide by 0");
}

Multi-Way Selections

Multi-way selections can be achieved by nesting two-way selections:

if (income < 0)
   System.err.println("income must be non-negative");
else if (income < 10000)
   tax = 0;
else if (income < 20000)
   tax = .2 * income;
else if (income < 35000)
   tax = .3 * income;
else if (income < 50000)
   tax = .5 * income;
else
   tax = .75 * income;

Question: What would a flow chart for this command look like?

Question: Why don't we need to check that income is also greater than or equal to the lower limit:

if (income < 0)
  System.err.println("income must be non-negative");
else if (0 <= income && income < 10000)
   tax = 0;
else if (10000 <= income && income < 20000)
   tax = .2 * income;
// etc.

Beware of the dangling else.

Question: How much tax does a person making $15,000 pay? (Answer = unknown.) $8000?  (Answer = $800.)

if (income < 10000)
if (5000 < income)
tax = .1 * income;
else tax = .2 * income;

Both Java and C++ inherit the bizarre switch command from C. The syntax and semantics of the switch command are so awkward that they almost render the command unusable. Here is the syntax:

switch(EXPRESSION)
{
   case EXPRESSION: COMMANDS
   case EXPRESSION: COMMANDS
   case EXPRESSION: COMMANDS
   // etc.
   default: COMMANDS
}

The expression in parenthesis, called the key, must produce an integer or integer subtype value when executed. The switch command contains one or more case clauses. Each case clause begins with an expression called a guard, followed by zero or more commands, called consequences. The last case clause traditionally begins with the reserved word "default". The list of commands following it are called alternatives:

switch(KEY)
{
   case GUARD: CONSEQUENCES
   case GUARD: CONSEQUENCES
   case GUARD: CONSEQUENCES
   // etc.
   default: ALTERNATIVES
}

Semantically, the key is evaluated. The value of the key is compared to the value of each guard, starting with the topmost guard. When a match is found, all commands following the guard, including commands that follow subsequent guards, are executed. The defaule guard matches all keys. For example, here is a flowchart describing the semantics of a switch command with three case clauses guarded by expressions called GUARD1, GUARD2, and GUARD3, respectively:

Notice that if KEY == GUARD1 is false, but KEY == GUARD2 is true (i.e., not 0 or false), then CONSEQUENCES1 will be ignored, but CONSEQUENCES2 and CONSEQUENCES3 will be executed. This happens even if KEY == GUARD3 is false!

To prevent the consequences of subsequent case clauses from being executed, programmers often end each sequence of consequences with a break command. Break commands will be discussed later. Essentially, a break command causes control to exit the current control command and pass to the next command.

Here's an example of a switch command:

switch(op)
{
   case '+': result = arg1 + arg2; break;
   case '*': result = arg1 * arg2; break;
   case '-': result = arg1 - arg2; break;
   case '%': result = arg1 % arg2; break;
   case '/':
      if (arg2) result = arg1 / arg2;
      else System.err.println("can't divide by 0");
      break;
   default: System.err.println("unrecognized operator " + op);
}

We can only use this switch command if op happens to be declared as a character, since char is a subtype of int:

double arg1, arg2, result;
char op; // = operator used to combine arg1 & arg2

If we declare op as a string:

string op;

then we must use nested if commands.

Sequence Commands

What happens if several commands must be executed when a condition is true:

if (CONDITION)
   COMMAND1;
   COMMAND2;
   COMMAND3;

Unfortunately, the compiler ignores indentation. Commands 2 and 3 will be executed if CONDITION is true or false.

However, most languages allow programmers to group a sequence of commands into a single command called a sequence. Syntactically, a sequence is just a list of commands, each terminated by a semi-colon, delimited by curly braces:

{ COMMAND; COMMAND; COMMAND; etc. }

Semantically, each command in a sequence is executed in order of appearance:

The following example shows how several commands can be grouped into a single command serving as the consequence of a conditional. Note that this example also shows that commands can be nested inside of commands. It also shows that sequences may contain declarations. A sequence that contains declarations is called a block. These will be discussed in the next chapter:

if (rad > 0)
{
   int min = center + rad;
   int max = center + rad;
   if (min < result && result < max)
   {
      double error = fabs(result - center);
      System.out.println("you only missed by " +
         error + " inches");
   }
}

Example 1 Calculators

import java.util.Scanner;

public class Calculator {


   public static void main(String[] args) {

      Scanner kbd = new Scanner(System.in);
      System.out.print("enter an arithmetic expression: ");
      if (!kbd.hasNextDouble()) {
         System.err.println("First argument must be a double");
         return;
      }
      double arg1 = kbd.nextDouble();
      String op = kbd.next();

      if (!kbd.hasNextDouble()) {
         System.err.println("Second argument must be a double");
         return;
      }
      double arg2 = kbd.nextDouble();

      if (op.equals("+")) {
         System.out.println("result = " + (arg1 + arg2));
      } else if (op.equals("*")) {
         System.out.println("result = " + (arg1 * arg2));
      } } else if (op.equals("-")) {
         System.out.println("result = " + (arg1 - arg2));
      }} else if (op.equals("/")) {
         System.out.println("result = " + (arg1 / arg2));
      }
      else {
         System.err.println("Unrecognized operator: " + op);
      }

   }
}

Example 2: bank Accounts

 

public class BankAccount {
   private double balance;
   private String password;
   public BankAccount(String pswd) {
      password = pswd;
      balance = 0;
   }
   private boolean isAuthorized(String pswd) {
      return password.equals(pswd);
   }
   public double getBalance(String pswd) {
      if (isAuthorized(pswd)) {
         return balance;
      } else {
         System.err.println("password error");
         return 0;
      }
   }
   public void deposit(double amt, String pswd) {
      if (isAuthorized(pswd)) {
         balance = balance + amt;
      } else {
         System.err.println("password error");
      }
   }
   public void withdraw(double amt, String pswd) {
      if (isAuthorized(pswd) && amt <= balance) {
         balance = balance - amt;
      } else {
         if (amt <= balance) {
            System.err.println("password error");
         } else {
            System.err.println("insufficient funds");
         }
      }
   }

}