slanted W3C logo

Day 14 — if-else

In today's lecture we discuss the concept of variable scope and how it impacts statements that have blocks of code (like if). We also look at a more general form of the if statement, as well as more general boolean expressions.

Reading Week

Succeed in Science (you should register if you plan on attending).

Midterm labtests from last year will be posted (see the main course website).

Regular office hours (3:30—5:00pm Wed and Thurs).

Recap: if

      if (logical-expression)
      {
         statement(s)
      }

The if statement lets you divert program flow based on a logical-expression; if the expression is true then program flow goes to the block of statements.

Blocks

Pairs of braces { } denote blocks of code. A variable declared inside a block is usable everywhere after the declaration inside the block. Consider our template code for example:

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

public class BlockExample1
{
   public static void main(String[] args)
   {
      PrintStream output = System.out;
      Scanner input = new Scanner(System.in);


   }
}

The reference variables output and input are usable everywhere inside the block of black braces (everywhere after they have been declared that is)

Outside of the block of black braces, however, the reference variables output and input cannot be used. We say that the black braces denote the scope of the variables output and input.

Nested Scope

Suppose we add a block of code inside the main method after the declarations of output and input:

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

public class BlockExample1
{
   public static void main(String[] args)
   {
      PrintStream output = System.out;
      Scanner input = new Scanner(System.in);

      float userValue = input.nextFloat();

      {
         output.print(userValue);
      }
   }
}

Inside the purple block, we are allowed to use the variables output and userValue because we are inside of their scope (between the black braces after the variables were declared).

Nested Scope

We can also declare variables inside of the purple block.

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

public class BlockExample1
{
   public static void main(String[] args)
   {
      PrintStream output = System.out;
      Scanner input = new Scanner(System.in);

      float userValue = input.nextFloat();

      {
         output.print(userValue);

         float absValue = Math.abs(userValue);
         output.printf(" and its absolute value %f%n", absValue);
      }
   }
}

Nested Scope

The variable absValue is local to the purple block because it can only be used inside of the purple block (the scope of absValue is the purple block). Trying to use absValue outside of its scope will result in a compilation error:

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

public class BlockExample1
{
   public static void main(String[] args)
   {
      PrintStream output = System.out;
      Scanner input = new Scanner(System.in);

      float userValue = input.nextFloat();

      {
         output.print(userValue);

         float absValue = Math.abs(userValue);
         output.printf(" and its absolute value %f%n", absValue);
      }

      // error! absValue is out of scope
      float signum = userValue / absValue;
   }
}

Nested Scope

One way to fix the compilation error on the previous slide is to declare a second variable named absValue after the purple block:

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

public class BlockExample1
{
   public static void main(String[] args)
   {
      PrintStream output = System.out;
      Scanner input = new Scanner(System.in);

      float userValue = input.nextFloat();

      {
         output.print(userValue);

         float absValue = Math.abs(userValue);
         output.printf(" and its absolute value %f%n", absValue);
      }

      float absValue = Math.abs(userValue);
      float signum = userValue / absValue;
   }
}

In this example, it looks like we are re-declaring a variable named absValue, which should be illegal. What is actually happening is that we are declaring absValue for the first time in the black block; thus, the declaration is legal.

Nested Scope

A second solution would be to declare absValue before the purple block:

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

public class BlockExample2
{
   public static void main(String[] args)
   {
      PrintStream output = System.out;
      Scanner input = new Scanner(System.in);

      float userValue = input.nextFloat();
      float absValue;

      {
         output.print(userValue);

         absValue = Math.abs(userValue);
         output.printf(" and its absolute value %f%n", absValue);
      }

      float signum = userValue / absValue;
   }
}

In this example, declaring a local variable in the purple block named absValue would have been illegal because absValue was declared earlier in the black block (the enclosing scope).

Find the Errors

int speed = 100;
{
   speed = 25
   {
      double speed = 250.0;
   }
   speed = 15.0;
   {
      speed = 1;
      {
         speed = 2;
      }
   }
}
speed = 150;

What Gets Printed?

char grade = 'B';
System.out.print(grade);
{
   System.out.print(grade);
   {
      grade = 'C';
      System.out.print(grade);
   }
   System.out.print(grade);
}
System.out.print(grade);

Why Do We Care About Blocks?

Because the if statement injects a block into your code.

public class BlockExample3
{
   public static void main(String[] args)
   {
      PrintStream output = System.out;
      Scanner input = new Scanner(System.in);

      float userValue = input.nextFloat();

      if (userValue < 0f)
      {
         float absValue = -userValue;
      }

      // error! absValue is out of scope
      float signum = userValue / absValue;
   }
}

To fix the above example, you need to move the declaration of absValue outside and before the if statement.

      float absValue = userValue;
      if (userValue < 0f)
      {
         absValue = -userValue;
      }

if-else

An if-else statement diverts program flow into two separate blocks.


      if (logical-expression)
      {
         statement(s)
      }
      else
      {
         statement(s)
      }

If the logical-expression is true then program flow goes to the purple block; otherwise program flow goes to the orange block.

Example

In Ontario, most hourly employees earn overtime pay after they have worked 44 hours in a work week. Overtime time is 150% of the employee's regular rate of pay. Write a program that calculates an employee's total pay based on a user-input number of hours worked in the week and the regular hourly wage. Your program should output both the regular-time pay and the overtime pay.

1. ask for the number of hours worked
2. get the number of hours worked
3. ask for the regular hourly wage
4. get the regular hourly wage
5. if the number of hours worked > 44
     regular pay = 44 * hour wage
     overtime pay = (number of hours worked - 44) * hourly wage * 1.5
   else
     regular pay = number of hours worked * hourly wage
     overtime pay = 0
6. output the result

Below we show only the implementation of Step 5.

      final double FULL_WEEK = 44.0;
      final double OVERTIME_RATE = 1.5;
      double regularPay;
      double overtimePay;
      if (hours > FULL_WEEK)
      {
         regularPay = FULL_WEEK * hourlyWage;
         overtimePay = (hours - FULL_WEEK) *
                       hourlyWage * OVERTIME_RATE;
      }
      else
      {
         regularPay = hours * hourlyWage;
         overtimePay = 0.0;
      }

Why are the variables regularPay and overtimePay declared before the if-else statement?

Can you rewrite Step 5 using only an if statement?

Multiway Branching

When you need to divert program flow to more than two paths of execution you use the if-else statement with a second if-else statement going in each else block.

import java.io.PrintStream;
import java.util.Scanner;
import type.lib.ToolBox;

public class LetterGrade
{
   public static void main(String[] args)
   {
      PrintStream output = System.out;
      Scanner input = new Scanner(System.in);
      
      output.print("Enter your numeric grade : ");
      int grade = input.nextInt();
      ToolBox.crash(grade < 0, "Grade is less than 0");
      ToolBox.crash(grade > 100, "Grade is greater than 100");
      
      final int GRADE_E = 40;
      final int GRADE_D = 50;
      final int GRADE_DP = 55;
      final int GRADE_C = 60;
      final int GRADE_CP = 65;
      final int GRADE_B = 70;
      final int GRADE_BP = 75;
      final int GRADE_A = 80;
      final int GRADE_AP = 90;
      
      if (grade < GRADE_E)
      {
         output.println("F");
      }
      else if (grade < GRADE_D)
      {
         output.println("E");
      }
      else if (grade < GRADE_DP)
      {
         output.println("D");
      }
      else if (grade < GRADE_C)
      {
         output.println("D+");
      }
      else if (grade < GRADE_CP)
      {
         output.println("C");
      }
      else if (grade < GRADE_B)
      {
         output.println("C+");
      }
      else if (grade < GRADE_BP)
      {
         output.println("B");
      }
      else if (grade < GRADE_A)
      {
         output.println("B+");
      }
      else if (grade < GRADE_AP)
      {
         output.println("A");
      }
      else
      {
         output.println("A+");
      }
   }
}

Logical (or Boolean, or Conditional) Operators

Suppose you were writing a program where you needed to find the minimum of three unique real numbers stored in variables eig1, eig2, and eig3.

if (eig1 is less than eig2) AND (eig1 is less than eig3)
   min = eig1
else if (eig2 is less than eig1) AND (eig2 is less than eig3)
   min = eig2
else
   min = eig3

Logical (or Boolean, or Conditional) Operators

If a condition depends on two expressions being true you would use the Conditional-AND operator && to combine the two expressions:

      if ((eig1 < eig2) && (eig1 < eig3))
      {
         min = eig1;
      }
      else if ((eig2 < eig1) && (eig2 < eig3))
      {
         min = eig2;
      }
      else
      {
         min = eig3;
      }

Java evaluates the boolean expressions from left to right. For Conditional-AND, if the boolean expression on the left hand side is false, Java does not evaluate the boolean expression on the right hand side.

Logical (or Boolean, or Conditional) Operators

If a condition depends either of two expressions being true you would use the Conditional-OR operator || (two vertical bars) to combine the two expressions.

For example, suppose you are the quality control engineer at a food packaging plant. Your packaging machine is supposed to fill packages with a certain weight of product, and you need to reject packages that are too light or too heavy.

if (weight < target weight) OR (weight > target weight)
    reject package
      final double TARGET_WEIGHT = 500;
      if ((weight < TARGET_WEIGHT) || (weight > TARGET_WEIGHT))
      {
         output.println("Rejected!");
      } 

Java evaluates the boolean expressions from left to right. For Conditional-OR, if the boolean expression on the left hand side is true, Java does not evaluate the boolean expression on the right hand side.