Introduction to Computer Science

Start Lecture #7

Remark:

  1. Lab 1 part 1 was described last time. It is due monday, 4 October.
  2. Lab 1 part 2 will be demoed in class today.

4.2.2: Loop Design Strategies

There are four parts to a loop and hence 4 tasks for the programmer to accomplish.

  1. Initialization (e.g., i = 0;)
  2. Continuation test (e.g., i < 10)
  3. Body
  4. Update for next iteration (e.g., i++)

As you have seen in Python and we shall see in Java, loops can be quite varied. For one thing the body can be almost arbitrary. For now let's assume that the body is the quadratic equation solver that we have written in class. Currently it just solves one problem and then terminates. How can we improve it to solve many quadratic equations?

final int n=10;
int i = 0;
while (i<n) {
  // input A, B, C
  // solve one equation
  i++;
}

n=getInput.nextInt(); int i = 0; while (i<n) { // input A, B, C // solve one equation i++; }
// input A, B, C while (A!=0 || B!=0 || C!=0) { // solve one equation // input A, B, C }
while (true) { // input A, B, C if (A==0 && B==0 && C=0) { break; } // solve one equation }

There are at least four techniques, each of which can be applied to many different problems.

  1. Hardwire the count.
    The code on the upper right has the count (10) within the code itself. (This can be done without n, by initializing i=10; testing i>0, and decrementing i--.)
  2. Read the count.
    We can input n rather that hardwiring it. This is illustrated in the second code sequence on the right. For these first two possibilities a for loop would be more convenient that an while (see section 4.4).
  3. Use a sentinel.
  4. Check for EOF (end of file).
    If, instead of Scanner, we use read(), the program can detect when we reach the EOF. In this way, we can continue to input problems and solve them until there are none remaining. A very simple example is in the optional 4.2.A; however, this requires a concept (exceptions) that we will not cover for quite a while.

4.2.3: An Advanced Math Learning Tool

4.2.3 (Alternate): Improving the Quadratic Solver

import java.util.Scanner;
public class Quadratic5 {
  public static void main (String[] Args) {
    System.out.println("Solving quadratic equations Ax^2 + Bx + C");
    // The next line is for hardwiring; we are reading the count
    // final int count = 10;
    int count;
    System.out.println("How many equations are to be solved?");
    Scanner getInput = new Scanner(System.in);
    count = getInput.nextInt();
    while (count-- > 0) {
      System.out.println("Enter real numbers A, B, C");
      double A = getInput.nextDouble();
      double B = getInput.nextDouble();
      double C = getInput.nextDouble();
      double discriminant = B*B - 4.0*A*C;
      double ans;
      if (A == 0) {
    System.out.println("A linear equation; only one root");
    ans = -C/B;
    System.out.println("The one root is " + ans);
      } else if (discriminant < 0) {
    System.out.println("No (real) roots");
      } else if (discriminant == 0) {
    System.out.println("One (double) root");
    ans = -B/(2*A);
    System.out.println("The double root is/are " + ans);
      } else {          // discriminant > 0
    double ans1 = (-B + Math.pow(discriminant,0.5))/(2.0*A);
    double ans2 = (-B - Math.pow(discriminant,0.5))/(2.0*A);
    System.out.println("The roots are " + ans1 + " and " + ans2);
      }
    }
  }
}

On the right is the code for a yet further improved quadratic equation solver. This code illustrates several of the points made above.

  1. Commented out is a line that would have been added to hardwire the count.
  2. As written, the count is read in. Thus the first two alternatives in 4.2.2 are shown.
  3. By executing
      while(count-- > 0))
    we ensure that the loop body, which inputs and solves one quadratic equation, is executed exactly count times. Be sure you understand why count--, was used, and why --count would not work.
  4. Notice how the code as written clearly separates the work of (inputting and) solving an equation from the work of ensuring that we solve the correct number of equations.
  5. When we officially learn about methods, we will see that we could pull out the body into a separate method.
  6. Alternatively we could write a method
      solve(A,B,C)
    That would solve one equation and print the results. This method would be called from the body of the loop right after the values of A, B, and C have been read it.

4.2.4: Controlling (i.e., Ending) a Loop with a Sentinel Value

import java.util.Scanner;
public class SumN {
  public static void main (String[] args) {
    Scanner getInput = new Scanner(System.in);
    double x, sum;
    int i, n;
    while (true) {
      System.out.println("How many numbers do you want to add?");
      n = getInput.nextInt();
      if (n < 0) {
        break;
      }
      System.out.printf("Enter %d numbers: ", n);
      i = 0;
      sum = 0;
      while (i++ < n) {
    x = getInput.nextDouble();
    sum += x;
      }
      System.out.printf("The sume of %d values is %f\n", n, sum);
    }
  }
}

On the right is a simple program to sum n numbers. Since it does not make sense to sum a negative number of numbers, we let n<o be the sentinel that ends the program.

There are again a few points to note.

  1. while(true) gives an non-ending loop sine the BE (boolean expression) is always true. So there must be some other way the loop ends.
  2. This is an n and 1/2 times loop: The first part of the body is executed one more time than the second part.
  3. break is used to break out of the loop, see section 4.9.
  4. This is actually a nested loop (loop inside a loop), which officially we haven't seen yet.
  5. The inner is itself a simple loop that sums n numbers.
  6. The outer loop is there since we want to sum n numbers several times, with possibly different values for n each time.

4.2.A: Ending a Loop with an EOF (End of File)

public class UseEOF {
  final static int EOF = -1;
   public static void main(String[] args) {
     int c;
     while ( (c = nextCharOrEOF()) != EOF) {
       System.out.write(c);
     }
     System.out.flush();
   }
   public static int nextCharOrEOF() {
      try { return System.in.read(); } 
      catch (java.io.IOException e) { return EOF; }
   }
}
  

On the right is a very simple loop that is terminated by EOF (end-of-file). However, it does use an advanced feature of Java, namely exceptions.

The method (think of function) nextCharOrEOF either returns the next character from System.in or it returns the value EOF.

EOF is not special; it is declared in the program.

The magic is the try/catch mechanism. The read() method will raise an exception, namely an IOException if the end of file is reached. This is caught by nextCharOrEOF and the value EOF is returned instead of the value returned by read().

4.2.5: Input and Output Redirections

As you know System.in is normally the keyboard and System.out is normally the display. However, they can be redirected to be an ordinary file in the file system. When System.in is redirected to a file f, input is taken from f instead of the keyboard, and when System.out is redirected to a file g, output goes to g.

For example the program UseEOF with the mysterious implementation, simply copies all its input to its output.

I copied it to i5 so we can run it (ignore how it works).

If we type simply  java UseEOF,  then whatever we type will appear on the screen.

If we type instead  java UseEOF <f,  then the file f will appear on the screen.

Similarly  java UseEOF <f >g copies the file f to the file g.

4.3: The do-while Loop

do-while

As we have seen a while loop repeatedly performs a test-action sequence: first a BE is tested and then, if the test passes, the body is executed.

do {
  stmts
} while (BE);

In particular, if the test fails initially, the body is not executed at all.

Most of the time, this is just what is wanted; however, there are occasions when we want the body to be first and the test second. In such situations we use a do-while loop.

We see the code skeleton for this loop on the near right and the flowchart on the far right.

final int quota = 250;
int count=0;
do {
  count++;
} while ((quota-=getInput.nextInt()) > 0);
System.out.printf("Needed %d values.\n", count);

4.3.A: Reaching a Quota

In the code on the right a quota is hardwired for brevity. Then the user inputs a series of numbers (say the dollar value of a sale). When he finally reaches or surpasses the quota, the program ends and reports how many values were needed.

4.4: The for Loop

for

Recall the four components of a loop as stated in 4.2.2

  1. Initialization (e.g., i = 0;)
  2. Continuation test (e.g., i < 10)
  3. Body
  4. Update for next iteration (e.g., i++)

Parts 1, 2, and 4 can all be written in one for statement. After the word for comes a parenthesize list with three elements separated by semicolons. The first component is the initialization, the second the test, and the third the update to prepare for the next iteration.

The first code shown on the right presents the for loop corresponding to the example mentioned in the list. Indeed, this is a common usage of for, to have a counter step through consecutive values.

for(i=0; i<10; i++){
  body
}

for(int i=0; i<10; i++)
for(i=0, j=n; i*j < 100; i++, j--)
for( ; ; )

The second for on the right shows that the loop variable can be declared in the for itself. One effect of this declaration is that the variable, i in this case, is not visible outside the loop. If there was another declaration of i outside, that variable would be inaccessible inside the loop.

It is also possible to initialize and update more than one variable as the third example shows.

Finally, the various components can be omitted, in which case the omitted item is essentially erased from the flowchart. For example, if the first component is omitted, there is no initialization performed; if the middle item is omitted there is no test (it is better to say that the test always yield true); and, if the third item is omitted there is no update. So the last, rather naked for will just keep executing the body until some external reason ends the loop (e.g., a break).

Homework: 4.5, 4.1 (the book forgot to show inputing the number of numbers), 4.9

4.5: Which Loop to Use?

They are equivalent. Any loop written with one construct can be written with either of the other two.

The for loop puts all the non-body in one place. If these pieces are small, e.g., for(i=0;i<n;i++), this is quite convenient. If they are large and or complicated, the while form is generally easier to read.

Much of this is personal preference. I don't believe there is a right or wrong answer.

4.6: Nested Loops

We have already done a nested loop in section 4.2.4.

for (int i=1; i<n-1; i++) {
  for (int j=i+1; j<n; j++) {
    // if the ith element exceeds the jth,
    // swap them
  }
}

It is essentially the same as in Python or any other programming language. The inner loop is done repeatedly for each execution of the outer loop.

For example, once we learn about arrays, we will see that the code on the right is a simple (but, alas, inefficient) way to sort an array.

4.7: Minimizing Numeric Errors

This concerns the hazards of floating point arithmetic. Although we will not be emphasizing numeric errors, two general points can be made.

  1. When comparing floating point values do not use equality (or inequality) testing. Either
  2. When adding many floating point values try to add the smaller ones first.

4.8: Case Studies

4.8.1: Finding the Greatest Common Divisor

Given two positive integers, the book just tries every number from one up to the smaller input and chooses the largest one that divides both of the inputs.

Let try a different way to get the GCD. Keep subtracting the smaller from the larger until you get a zero, at which point the nonzero value is the GCD (if they are equal call one of them larger).

(54, 36) → (18, 36) → (18, 18) → (18, 0).
(7, 6) → (1, 6) → (1, 5) → ... → (1, 1) → (1, 0).

4.8.2: Predicting the Future Tuition