previous
 next 
CS 1063  Weeks 8 and 9:  Conditional Execution 
Fall 2014: Sections 1 and 2

Today's News: October 13
This is the start of Week 8
Lab 4 due Tuesday

Objectives

Assignments

Contents



if, if/else, and Nested if/else Statements


An if statement executes a controlled statement (or a group of controlled statements) only if a test is true.  A test can also be called a condition or a Boolean expression.  Here is an example of an if statement:
System.out.print("Enter your GPA: ");
double gpa = console.nextDouble();
if (gpa >= 2.0) {
    System.out.println("Good Standing");
}

The test (gpa >= 2.0) determines whether the value of gpa is greater than or equal to 2.0.  If true, Good Standing is printed.  If false, then this if statement does nothing.

The syntax of an if statement is:
if (test) {
  statement(s);
}

An if/else statement executes one branch (one alternative) if a test is true, or another branch the test is false.  For example:
System.out.print("Enter your GPA: ");
double gpa = console.nextDouble();
if (gpa >= 2.0) {
    System.out.println("Good Standing");
} else {
    System.out.println("Academic Warning, Probation, or Dismissal");
}

In this example, if (gpa >= 2.0) is false, then Academic Warning, Probation, or Dismissal is printed.

The syntax of an if/else statment is:
if (test) {
  statement(s);
} else {
  statement(s);
}

A nested if/else statement performs a series of tests.  The code below executes the branch that corresponds to the first test that is true.  If no test is true, this statement executes the branch that corresponds to the else.  For example:
System.out.print("Enter your GPA: ");
double gpa = console.nextDouble();
if (gpa > 4.0) {
    System.out.println("Your GPA cannot be higher than 4.0");
} else if (gpa >= 2.0) {
    System.out.println("Good Standing");
} else if (gpa >= 0.0) {
    System.out.println("Academic Warning, Probation, or Dismissal");
} else {
    System.out.println("Your GPA cannot be lower than 0.0");
}

This example has four branches, only one of which will be executed.  Determining which one requires more complex reasoning.
  1. If (gpa > 4.0) is true, then Your GPA cannot be higher than 4.0 is printed.

  2. If (gpa > 4.0) is false and (gpa >= 2.0) is true, then Good Standing is printed.

  3. If (gpa > 4.0) and (gpa >= 2.0) are both false and (gpa >= 0.0) is true, then Academic Warning, Probation, or Dismissal is printed.  If this branch is executed, we know that gpa is also lower than 2.0 because, to get to this branch, (gpa >= 2.0) must have been false.

  4. If (gpa > 4.0), (gpa >= 2.0), and (gpa >= 0.0) are all false, then Your GPA cannot be lower than 0.0 is printed.  If this branch is executed, we know that gpa is less than zero because, to get to this branch, (gpa >= 0.0) must have been false.

The syntax of a nested if/else statment is:
if (test) {
    statement(s);
} else if (test) {
    statement(s);
} ...
  else {
    statement(s);
}

Simple if/else

Activity: Midterm Grades
Midterm grades are calculated from a courseAvg which is of type double.
At a given time in the semester, the courseAvg will have a maximum value that depends on how many quizzes, labs, projects, and exams have been done.
Midterm grades are calculated using the following cutoffs:
At leastMidterm Grade
90%A
80%B
70%C
65%C-
60%D
Any average below 60% of the maximum grade is an F.
  1. Write a code segment that prints the grade if the maximum value of courseAvg is 31.0.
  2. Write a method that takes two double parameters, a course average and a maximum course average, and returns a String representing the corresponding letter grade.

Relational Operators

Two numbers can be compared using relational operators.  You can also compare an int or double variable to a number or compare two int or double variables.  In general, you can compare any two numeric expressions.
Operator     Meaning Example True     Example False
== equals 2 == 2 3 == 4
!= not equal 3 != 4 2 != 2
< less than 3 < 4 2 < 2
<= less than or equal to 2 <= 2 4 <= 3
> greater than 4 > 3 2 > 2
>= greater than or equal to     2 >= 2 3 >= 4
Activity: Relational Operators
Given
int x = 4;
int y = -3;
int z = 4;
What is the result of each of the following expressions?  Note that the relational operators have a lower precedence than the arithmetic operators.
a) x == 4
b) x == y
c) x == z
d) y == z
e) x + y > 0
f) x - z != 0
g) y * y <= z
h) y / y == 1
i) x * (y + 2) > y - (y + z) * 2
Try to come up with different values for x, y, and z that will have the opposite results.

Activity: Relational Operators

Logical Operators

Tests can be combined using the logical operators && (AND) and || (OR).  The && operator requires that both tests be true.  One common use of && is to test if a value is within some range, for example, the following test is true if score is between 0 and 100.
(score >= 0 && score <= 100)

The || operator requires that one or both tests be true.  A common use of || is to test whether a value is equal to one of several alternatives. For example, the following test is true if score is equal to 99 or equal to 100.
(score == 99 || score == 100)

Another common use of || is to test if a value is outside some range, for example, the following test is true if score is less than 0 or greater than 100 (that is, not between 0 and 100).
(score < 0 || score > 100)

In the above examples, note that the logical operators && and || have a lower precedence than the relational operators.  Here is a revised GPA program using various relational and logical operators.

import java.util.*;

public class GPA {
    public static void main(String[] args) {
        Scanner console = new Scanner(System.in);
        System.out.print("Enter your GPA: ");
        double gpa = console.nextDouble();
        if (gpa > 4.0 || gpa < 0.0) {
            System.out.println(gpa + " is an invalid value for GPA");
        } else {
            System.out.println(gpa + " is an valid value for GPA");
            if (gpa <= 4.0 && gpa >= 2.0) {
                System.out.println("Good Standing");
            } else {
                System.out.println("Academic Warning, Probation, or Dismissal");
            }
        }
    }
}

Activity: UTSA Bulleitn
Use the information in the UTSA Information Bulletin to write programs for one or more of the following tasks:
  • Classify an undergraduate student as a senior, junior, sophomore, or freshman.
  • Determine the undergraduate time status of a student.
  • Determine the level of a course from its number.
  • Determine whether a student makes the President's List, Dean's List, or Honor Roll.
  • Determine whether a student graduates cum laude, magna cum laude, or summa cum laude.
Be sure to ask the user for the information that is needed for the task.  Also, be sure to identify invalid values.

Logical Operators

Today's News: October 15
Quiz 6 is available, due Saturday

Cumulative Algorithms

Cumulative sum

How would you find the sum of all integers from 1 to 1000?
int sum = 1 + 2 + 3 + 4 + ... ; // How long is this line?
System.out.println("The sum is " + sum);
What if we want the sum from 1 to 1,000,000?  Consider the pseudocode to add one number at a time to sum.
Initialize sum to 0.
Add 1 to sum.
Add 2 to sum.
...
Add 1000 to sum.
Clearly, counting from 1 to 1000 can be performed in a for loop.
Initialize sum to 0.
For the numbers from 1 to 1000:
    Add the number to sum.

This pseudocode can be translated into a few short lines of Java.
int sum = 0;
for (int i = 1; i <= 1000; i++) {
    sum += i;  // why not sum = i;
}
Activity: SumNumbers
Write a SumNumbers class that inputs two numbers from the user and sums the numbers from the first number to the second number.
Note: Strange things will happen if the sum exceeds 2,147,483,647.

Sum of odd


More generally, we want to sum numbers that may not be in consecutive sequence.  In this case, the pseudocode is:
Initialize sum to 0.
For however many numbers we want to sum:
    Obtain the next number.
    Add the number to sum.

The book includes a program that adds numbers from user input.  Here is a variation.
import java.util.*;

public class SumInput {
    public static void main(String[ ] args) {
        System.out.println("This program adds a sequence of numbers.\n");
        Scanner console = new Scanner(System.in);
        System.out.print("How many numbers do you have? ");
        int totalNumber = console.nextInt( );

        double sum = 0.0;

        for (int i = 1; i <= totalNumber; i++) {
            System.out.print("Enter the next number: ");
            double next = console.nextDouble( );
            sum += next;
        }

        System.out.println("\nsum is " + sum);
    }
}

You should trace the program for an example sequence of inputs, keeping track of the values of the variables (including i and next).  What will happen if the user enters a zero or a negative number?
Activity: Modify SumInput 1
Modify the SumInput program so it computes the average.  Beware of dividing by zero.

Activity: Modify SumInput 2
Modify the SumInput program so it also finds the product of the inputs (all the inputs multiplied together).

Often, we want to do additional processing in a cumulative algorithm.  For example, we want might to count how many inputs are negative.

Counting the values that satisfy a test can be accomplished with an integer variable called a counter and an if statement:
  1. Declare and initialize the counter before the loop.
    int negatives = 0;
    
  2. Increment the counter inside a loop if the number satisfies the test.
    if (next < 0) {
        negatives++;
    }
    
  3. Report (or return or use in some way) the value of the counter after the loop.
    System.out.println("There are " + negatives + " negative numbers.");
    
Activity: Modify SumInput 3
Modify the SumInput program to count negative numbers.  Also, use an if statement so the report is grammatical if there is one negative number.

Activity: Modify SumInput 4
Modify the SumInput program to count other kinds of numbers.

Book:
Read the subsection on roundoff error.  Use the SumInput program to experiment with roundoff errors.

Min/Max Loops

Consider finding the maximum and/or minimum value of a sequence of numbers.  If we look at one number at a time, we can keep track of the largest value so far and update it if the current number is larger.

Traditionally, max is used as the variable name for the largest value so far (likewise min for the smallest so far).  What should max be initialized to?  Initializing max to zero would be a mistake if all of the numbers are negative.  Similarly, initializing min to zero would be a mistake if all of the numbers are positive.  The safest solution is to initialize max (or min) to the first number.  This leads to the following pseudocode.
Initialize max to the first number.
For the remaining numbers:
    Obtain the next number.
    If this number is larger than max:
        then assign this number to max.

The following program finds the maximum of numbers input by the user.  Trace the program for an example sequence of inputs, keeping track of the values of the variables (including i and next).  What will happen if the user enters a zero or a negative number?
public class MaxInput {
    public static void main(String[ ] args) {
        System.out.println("This program finds the maximum of a sequence of numbers.\n");
        Scanner console = new Scanner(System.in);
        System.out.print("How many numbers do you have? ");
        int totalNumber = console.nextInt( );

        System.out.print("Enter the first number: ");
        double max = console.nextDouble( );

        for (int i = 2; i <= totalNumber; i++) {
            System.out.print("Enter the next number: ");
            double next = console.nextDouble( );
            if (max < next) {
                max = next;
            }
        }

        System.out.println("\nmax is " + max);
    }
}
Activity: Modify MaxInput 1
Modify the MaxInput program so it also finds the minimum.

Activity: Modify MaxInput 2
Modify the MaxInput program so it also finds the second largest number.

Max and Min

Today's News: October 17
Quiz 6 is available, due Saturday
Lab 5 due on Tuesday

Text Processing

The char Type


The primitive type char represents one character.  Literal values are expressed by a character within single quotes, for example: 'A', '7', '#'.

Values of type char are represented by integers, and arithmetic operators can be applied to chars, but generally this is not considered good practice (exceptions include using ++ to get the next char).  A char can be stored in an int, but a cast is required to store an int in a char.
int code = 'a';
char c = (char) 65;

Activity: Write a program that prints the characters corresponding to the integers from 32 to 126.

The Character class has many useful functions for operating on chars.  These methods are called by putting Character. in front and including the char as a parameter (similar to how Math methods are called).
Method Name Return Type     Description
getNumericValue(ch)     int returns a number if ch is a digit
isDigit(ch) boolean returns true if ch is a digit
isLetter(ch) boolean returns true if ch is in 'A' to 'Z' or 'a' to 'z'
isLowerCase(ch) boolean returns true if ch is a lowercase letter
isUpperCase(ch) boolean returns true if ch is an uppercase letter
toLowerCase(ch) char returns the lowercase version of ch
toUpperCase(ch) char returns the uppercase version of ch

chars are primitive types, and Strings are objects in Java, and it can be confusing for beginners to keep track of the differences.  Here are some important things to remember.
Activity: Write a program to try out the other Character methods and the corresponding String methods.

char type

Cumulative Text Algorithms


We can process each character of a String by calling the charAt method for each index from 0 up to (but not including) the length of the String.
String string1 = "Four score and seven years ago";
for (int i = 0; i < string1.length( ); i++) {
    char c = string1.charAt(i);
    System.out.println("char '" + c + "' is at index " + i);
}

For example, we might to count the number of times a particular character occurs in a String.  Recall that to use a counter, we need to initialize the counter before the loop, increment the counter inside the loop if a test is satisfied, and use the value of the counter after the loop.  A method that returns the count would have the following pseudocode.
  1. Initialize the counter to 0.
  2. For each char in the String:
    1. Obtain the next char.
    2. If this char is equal to the one we want to count:
      1. then increment the counter.
  3. Return the value of the counter.

The String to be searched and the char to look for need to be parameters.  Otherwise, how do we know what to search and what to search for?  This leads to the following method.
public static int countChar(String s, char c) {
    int count = 0;
    for (int i = 0; i < s.length( ); i++) {
        if (c == s.charAt(i)) {
            count++;
        }
    }
    return count;
}
Activity: Write a program to ask the user for a String and a char, to call the countChar method, and print the result.  Use the nextLine method of the Scanner class to read the String.

Activity: Modify the countChar method so it counts both lowercase and uppercase versions of a char.

Activity: Write a countDigits method that counts the number of digits in a String.


For another example, consider converting a String to "leet", a geeky variation of spelling sometimes seen on the internet.  Part of the conversion is substitutions, for example: 'e' to '3', 'l' to '1', and 't' to '7'.  Given "turtle", we want to return "7ur713".  As we examine the String parameter one letter at a time, we want to add one letter (or a substitution) to the String we want to return.
Initialize the result to "".
For each char in the String:
    Obtain the next char.
    If this char needs to be substituted:
        then assign the substitution to the char.
    Add (concatenate) the char to the end of the result.
Return the result.

The pseudocode only has one "if statement", but in Java we will need three different conditions for 'e', 'l', and 't'.  Recall that we can use the + operator to produce a String from two other Strings (or a String and any other type).
public static String partialLeet(String str) {
    String result = "";
    for (int i = 0; i < str.length( ); i++) {
        char c = str.charAt(i);
        if (c == 'e' || c == 'E') {
            c = '3';
        } else if (c == 'l' || c == 'L') {
            c = '1';
        } else if (c == 't' || c == 'T') {
            c = '7';
        }
        result = result + c;
    }
    return result;
}

First look at how the String variable result behaves like a counter.  Before the loop, result is initialized to "", aka the empty string, a String with zero characters.  Inside the loop, the char variable c is added (concatentated) to the end of result.  After the loop, result is returned.

Next look at how the char variable c is manipulated in the loop.  In each iteration, c is initialized to the char at index i.  The if statement changes the value of c under certain conditions.  Changing the value of c does not change str; a primitive type is a copy of the original value.  In the tests, note that lower case and upper case letters are checked.  If none of the tests are true, c's value will stay the same.
Activity: Write a program to ask the user for a String, to call the partialLeet method, and print the result.

Activity: Modify the partialLeet method so it includes the substitutions: 's' to 5 and 'z' to 2.  Look up some other substitutions and add them to your method.

Activity: Write and test a switchCase method to reverse lowercase and uppercase in a String.  For example, it should convert "Java" to "jAVA".

Cumulative Text Algorithms

Today's News: October 20
Lab 5 due on Tuesday

Methods with Conditional Execution

Preconditions, Postconditions, and Exceptions


When you write a method, you should consider what needs to be true for the method to work and what it means for the method to accomplish its task.  For example, a method that finds the square root of a number might need the number to be zero or positive, and its return value, when multiplied by itself, should be equal to (or be very close to) the number.

A precondition is a condition that must be true when the method is called so that the method can perform its task.  A postcondition is a condition that will be true when the method is finished as long as the method's preconditions were true.

Consider writing a method that is supposed to return the sum of the integers from 1 up to a given value.  For example, if the parameter is 7, the method should return the value of 1+2+3+4+5+6+7.  Note that stating what a method is supposed to do usually describes its postcondition.
public static int sumUpTo(int n) {
    int sum = 0;
    for (int i = 1; i <= n; i++) {
        sum += i;
    }
    return sum;
    // postcondition: sum is the sum of the integers from 1 up to n
}

What should this method do if the parameter is zero or negative?  It doesn't make sense to add up the integers from 1 up to -5.  If we think that a zero or negative parameter is a programming error, then we should state that a positive parameter is a precondition of this method.
public static int sumUpTo(int n) {
    // precondition: n >= 1
    int sum = 0;
    for (int i = 1; i <= n; i++) {
        sum += i;
    }
    return sum;
    // postcondition: sum is the sum of the integers from 1 up to n
}

Unfortunately, comments don't do anything when the code is executed.  This method will return 0 if the precondition is not satisfied.  If this is a serious enough error, then we want to throw an exception, which will force the programmer to fix the bugs.
public static int sumUpTo(int n) {
    // precondition: n >= 1
    if (n < 1) {
        throw new IllegalArgumentException(n + " is not positive");
    }
    int sum = 0;
    for (int i = 1; i <= n; i++) {
        sum += i;
    }
    return sum;
    // postcondition: sum is the sum of the integers from 1 up to n
}

You see the new keyword because an exception is an object in Java.  There are many different kinds of exceptions in Java, but using IllegalArgumentException should be sufficient for now.
Activity: Square Root
Write and test a squareRoot method that returns the value of Math.sqrt, but throws an exception whenever the parameter is negative.

Activity: Percent Correct
Write and test a percentCorrect method that has two int values as parameters: the number of questions on the test and the number of correct answers by a student.  The method should return the percentage of correct answers.  Identify the postcondition(s) and precondition(s).  Your method should throw an exception if any precondition is not satisfied.

Activities: Square Root and Percent Correct


Today's News: October 22
Quiz 7 is available, due Saturday

Return Values and Code Paths


One of the interesting features of the return statement is that when a return statement is run, the method does not run any more code.  For example, consider this method which behaves like Math.max.
public static double max(double a, double b) {
    if (a > b) {
        return a;
    }
    return b;
}

This method returns the larger of the two doubles.  It looks like the method will always return b because that is what the last statement does.  However, if the test a > b is true, then the first return statement is executed and any code following the if statement is skipped.  There are two different paths in the code that could be executed: one path is executed if a > b, and the other path is executed if a <= b.
Activity: Identify the four different code paths in the following method.  Generate examples that go down each path.  Be sure to consider examples where all three parameters are different, where all three parameters are equal, and where two of the three parameters are equal.  Determine the conditions that correspond to each path.
public static double max(double a, double b, double c) {
    if (a > b) {
        if (c > a) {
            return c;
        } else {
            return a;
        }
    }
    if (c > b) {
        return c;
    }
    return b;
}

Activity: Write and test a method to perform one of the following tasks.
  • Return the minimum of two numbers.
  • Return the minimum of three numbers.
  • Given two numbers, return the number closest to zero.
  • Given three numbers, return the number closest to zero.
  • Return the median of three numbers.

Reasoning about code paths is usually done when you are planning how to write a method.  Suppose we want to write a method that returns the first digit in a String.  This will probably involve a loop that obtains and examines each char in the String.
For each char in the String:
    Obtain the next char.
    Process the char

Note that if the first char in the String is a digit, the method can return that char and does not need to look at the remaining characters.  In general, once the method finds a digit, we can return it without looking at any remaining characters.
For each char in the String:
    Obtain the next char.
    If the char is a digit:
        Return that char.

Having a return statement in a loop creates many different code paths.  For example, if the steps return on the fourth iteration, then, the fourth character must be a digit, and, in addition, the first three characters were not digits.

What should we do if the String has no digits?  Because the method will be returning a char, we could return a non-digit char (a '?', for example) to indicate that no digit was found (throwing an exception is another possibility).  Note that the method should not return a '?' until all the chars have been examined.  This is not something we can do before the loop (haven't examined any chars yet) or inside the loop (there might be more chars to examine), so it must be done after the loop.  That is, if the loop ends without a return, that means there were no digits found.
For each char in the String:
    Obtain the next char.
    If the char is a digit:
        Return that char.
Return '?' if the method gets to this point.

With all these considerations, we can convert the pseudocode into a method.
public static char firstDigit(String s) {
    for (int i = 0; i < s.length(); i++) {
        char c = s.charAt(i);
        if (Character.isDigit(c)) {
            return c;
        }
    }
    // to get to this point,
    // no return statements have been executed,
    // so none of the chars was a digit
    return '?';
}
Activity: Finding First or Last
Write and test a method to perform one of the following tasks.
  • Return the first lowercase letter in a String.
  • Return the first even digit in a String.
  • Return the position of the first digit in a String.
  • Return the last digit in a String.

Activity: Finding First or Last