Introduction to Computer Science

Start Lecture #6

3.15: switch Statements

We have seen how to use if-then-else to specify one of a number of different actions, with the chosen action determined by a series of Boolean expressions.

Often the Boolean expressions consist of different values for a single (normally arithmetic) expression.

switch(expression) {
  case value1: stmts1
  case value2: stmts2
  ...
  case valueN: stmtsN
  default:  def-stmts
}

For example we may want to do one action if i+j is 4, a different action if i+j is 8, a third action if i+j is 15, and a fourth action if i+j is any other value. In such cases the switch statement, shown on the right, is appropriate.

When a switch statement is executed the expression is evaluated and the result specifies, which case is executed.

The semantics of switch are somewhat funny. Specifically, given the similarity with if-then-else, one might assume that after stmts1 is executed, control transfers to the end of the switch. However, this is wrong: Unless explicitly directed, control flows from one case to another.

The semantics of the C/Java switch closely resemble those of an assembler-level jump table and the computed goto of Fortran.

switch (expression) {
  case value1: stmts1
               break;
  case value2: stmts2
               break;
  ...
  case valueN: stmtsN
               break;
  default:  def-stmts
}

It is quite easy to explicitly transfer control from the end of one case to the end of the entire switch. Java, again borrowing from C, has a break statement that serves this purpose.

We see on the right the common form of a switch in which the last statement of stmts1...stmtsN is a break.

When a break is executed within a switch, control breaks out of the switch. That is, execution proceeds directly to the end of the switch. In particular, the code on the right will execute exactly one case or the default.

Homework: 3.11, 2.25, Handout for those w/o 8e.

3.16: Conditional Expressions

bool-expr ? expr1 : expr2

if (x==5) y = 2; else y = 3;
y = x==5 ? 2 : 3;

Recall that Java has += that shortens x=x+5 to x+=5. Java has another shortcut, the tertiary operator ?...: that can be used to shorten an if-then-else.

The general form of the so-called conditional expression is shown on the top right. The value of the entire expression is either expr1 or expr2, depending on the value of the Boolean expression bool-expr.

For example, the middle right if-then-else can be replaced by the bottom right assignment statement containing the equivalent conditional expression.

Note that this is not limited to arithmetic and can often be used to produce grammatically correct output.

  System.out.println ("Please input " + N + "number" + N ? "." : "s.");

I realize this looks weird; be sure you see how it works.

Homework: Redo 3.7, this time using a switch.

3.17: Formatting Console Output

A comparatively recent addition to Java is the C-like printf method. (It was added in Java 2 Standard Edition 5; we are using J2SE 6).

System.out.printf(format, item1, item2, ..., item n);

The first point to make is that printf() takes a variable number of arguments as indicated on the right. The required first argument, which must be a string, indicates how many additional arguments are needed and how those arguments are to be printed.

Wherever the first argument contains a %, the value of the next argument is inserted (a double %% is printed as a single %).

 
System.out.printf("i = %d and j = %d", i, j);
System.out.printf("x = %f and y = %e", x, y);

The first line on the right prints the integer values of x and y.
The second line prints two real values, the second using scientific notation.

Common Specifiers
SpecifierOutput


%bBoolean
%cCharacter
%dInteger
%fFloating Point
%eScientific Notation
%sString

The table on the right gives the most common specifiers. Although Java will perform a few conversions automatically (e.g., an integer value will be converted to a string if the corresponding specifier is %s), most conversions are illegal. For example

    int i;    double x;    int one=1, two=2;
    System.out.printf("Both %f and %d are bad\n", i, x);
    System.out.printf("%d plus %d is %d\n", one, two, one+two);
  

You could try to find which conversions are OK, but I very much suggest that instead you do not use any. That is use %f and %e only for floats and doubles; use %d only for the four integer types; use %s only for Strings, etc.

Width and Precision

System.out.printf("%d\n%d\n",45,7);

45 7
45 7
System.out.printf("%2d\n%2d\n",45,7);
System.out.printf("%2d\n%2d\n",45,789);
45 789

Note that %d uses just enough space to print the actual number; it does not pad with blanks. If you print several integers one per line using %d they won't line up properly. For example, the printf() on the right will produce the first output, not the second.

To remedy this problem you can specify a minimum width between the % and the key-letter. For example the second printf() does produce the second output.

But what would happen if we tried to print 543 using %2d? There are two reasonable choices.

  1. Print something like ** the way a spread sheet displays a value in a cell that is too narrow
  2. Use three columns.

Java chooses the second option. So the bottom printf() produces the bottom output.

These same considerations apply to the other 5 specifiers in the table: if the value is not as wide as the specified width, the value is right justified and padded on the left with blanks.

For the real number specifiers %f and %e one can specify the precision in addition to the (minimum) width. This is done by writing the width, then a period, and then the precision between the % and the letter.

For example %6.2 means that the floating-point value will be written with exactly 2 digits after the decimal point and if the result (including a minus sign if needed) is less than 6 characters, the value will be padded with blanks on the left.

Left Justification

As we have seen, when the width is specified (with or without precision), the value is right justified if needed and padded on the left with blanks. This is normally just what you want for numbers, but not for strings.

For all specifiers (numbers, strings, etc) Java supports left justification (padding on the right with blanks) as well: Simply put a minus sign right before the width as in %-9f, %-10.2e, or %-7s.

3.18: Operator Precedence and Associativity

Operator Precedence in
decreasing order
OperatorAssociativity


var++, var--Left-to-right
+,- (unary), ++var, --varRight-to-left
(type)Right-to-left
!Left-to-right
*,/,%Left-to-right
+,- (binary)Left-to-right
<,<=,>,>=Left-to-right
==,!=Left-to-right
^Left-to-right
&&Left-to-right
||Left-to-right
?: (tertiary)Right-to-left
=,+=,-=,*=,/=,%=Right-to-left

The table on the right lists the operators we have seen in decreasing order of precedence. That mean that, in the absence of parentheses, if an expression contains two operators from different rows of the table, the operator in the higher row is done first.

The second column gives the associativity of the operators. If an expression contains two operators from the same row and that row has left-to-right associativity, the left operator is done first. Similarly if the row has right-to-left associativity, the right operator is done first. As with precedence, parentheses can be used to override this default ordering.

Not many programmers have the entire table memorized (not to mention the fact that there are other operators we have not yet encountered). Instead, they use parenthesis even when their use might not be necessary due to precedence and associativity.

However, it is wise to remember the precedence and associativity of some of the frequently used operators. For example, one should remember that *,/,% are executed before (binary +,-) and that both groups have left-to-right associativity. It would clutter up a program to see an expression like
((x*y)/z)/(((-x)+z)-y) rather than the equivalent (x*y/z)/(-x+z-y)


3.19: (Gui) Confirmation Dialogs

Chapter 4: Loops

4.1: Introduction

As you know from Python, loops are used to execute the same block of code multiple times. There are several kinds of loops: The while (and do-while) are fairly general; the for is most convenient when the loops are counting, but can be used (abused?) in quite general ways.

4.2: The while Loop

while (BE) {
  stmts
}

Some early languages (notable FORTRAN) did not have a while loop, but essentially all modern languages (including Fortran) do.

The semantics are fairly simple:
while

  1. The Boolean expression (BE) is tested. If it is false the while loop ends.
  2. If the BE is true, the statements (stmts) in the loop body are executed.
  3. The process repeats from the beginning.

The idea of a while loop is that the body is executed while (i.e, as long as) the Boolean expression is true.

The flow chart on the right illustrates these simple semantics.

Note that if the Boolean expression is false initially, the loop body (the statements in the diagram) are not executed at all. We shall see in the next section a loop in which the body is executed at least once.

Note that when right after the while loop is executed, the BE is FALSE.
Please don't get this wrong.

while ((i=getInput.nextInt) >= 0) {
  // process the non-negative i
}

For example, when the loop on the right ends (assuming no EOF), the value of i is negative!

Lets write a program that adds two positive numbers just using ++ and --. One solution is here.

This is actually how one defines addition starting with Peano's postulates

4.2.1: Guessing a Number

The program picks a random number between 0 and 100 inclusive (101 possibilities). The users repeatedly guess until they are correct. For each guess, the program states higher, lower, or correct.

Let's do in class; one solution is in the book.