Introduction to Computer Science

Start Lecture #9

Homework: 5.17, 5.35.

5.12: Method Abstraction and Stepwise Refinement

One big advantage of methods is that users of a method do not need to understand how the method is implemented. The user needs to know only the method signature (parameters, return value, thrown exceptions) and the effect of the method.

Although I remember enough calculus to derive the Taylor series for sine, that might not be the best way to implement sin(). Moreover, I would need to work very hard to produce an acceptable implementation of atan(). Nonetheless, I would have no problem writing a program accepting a pair (x,y) representing a point in the plane and producing the corresponding (r,θ) representation. To do this I need to just use atan, not implement it.

As mentioned previously, when given a problem that by itself is too complicated to figure out directly, it is often helpful to break it into pieces. A very simple example was in the previous section. Separating out pickChar() made the main program easy and pickChar itself was not very hard.

5.12.1: Top-Down Design

figure-5.12

We haven't done anything large enough to illustrate top-down design in all its glory. The idea is to keep breaking up the problem. That is, you first specify a few methods that would enable you to write the main() method.

Then for each of these new methods M you define other submethods that are used to implement M and the process repeats.

The figure on the right (a very slightly modified version of figure 5.12 from the 8e) shows this design philosophy applied to the problem of printing a calendar for any given month.

You can guess the functionality of most of the methods from their name (an indication of good naming). Only getTotalNumberOfDays requires explanation. It calculates the number of days from 1 January 1800 to the first day of the month read from the input.

5.12.2: Top-Down or Bottom-Up Implementation

Given a fleshed out design as in the diagram, we now need to implement all the boxes. There are two styles: top-down and bottom-up.

In the first, you perform a pre-order traversal of the design. That is, you start by implementing the root calling the (unimplemented) child methods as needed.

In order to test what you have done before you are finished, you implement stub versions of the methods that have been called but not yet implemented. For example, the stub for a void method such as printMonthBody might do nothing or might just print something like printMonthBody called.

Stubs for non-void methods need to return a value of the correct type, but that value need not be correct.

In a bottom-up approach, you perform a post-order traversal of the design tree. That is, you only implement boxes all of whose children have been implemented (we are ignoring recursive procedures here). In the beginning this means you implement a leaf of the tree.

Since you implement the main program last, you need to write test programs along the way to test what you have implemented so far.

5.12.3: Implementation Details

Read. In particular read over the full implementation of printCalendar.java

Chapter 6: Single-Dimensional Arrays

6.1: Introduction

6.2: Array Basics

Arrays are an extremely important and popular data structure that are found in essentially all programming languages. An array contains multiple elements all of the same type.

In this chapter we restrict ourselves to a 1-dimensional (1D) array, which is essentially a list of values of a given type. The next chapter presents the more general multi-dimensional array.

In Java the ith element of a 1D array named myArray is referred to as myArray[i]. Note the square brackets [].

6.2.1: Declaring Array Variables

main(String[] args)

In the example on the right, which we have used in every program we have written, the only parameter args is a 1D array of Strings.

Note the syntax, first the base type (the type of each element of the array), then [], and finally the name of the array.

double[] y;

When the array is not a parameter as above, but is instead a locally declared variable, the syntax is essentially identical and is shown on the right. The only difference being a semicolon added at the end.

You may recall that early in the course I sometimes wrote String args[] and noticed that it worked as well as String[] args. In fact this has been added to Java just so that C language programmers would be comfortable. However, the String[] args format is preferred.

6.2.2: Creating Arrays

There is an important difference between declaring scalars as we have done many times previously and declaring an array. When the scalar is declared, it can be used (i.e., given a value). For an array another step is needed.

double[] x;
x = new double[10];

double[] x = new double[10];

The declaration double[] x, declares x but does not reserve space for any of the elements x[i]. This must either be done separately as shown on the above right or written together as shown below the line.

The new operator allocates space, in the example on the right, enough space for 10 doubles. The effect is that the array x can hold 10 elements.

In java the first element always has index 0, so x consists of x[0], x[1], ..., x[9].

6.2.3: Array Size and Default Values

The size of an array is fixed; it cannot be changed.

To retrieve the size of a 1D array is easy, just write name.length. For example x.length would be 10 for the example above.

Another difference between arrays and scalars is that when the array is created (using the new operator), initial values are given to each element.

6.2.4: Array Indexed Variables

for (int i=0; i<x.length; i++) {
  x[i] = 34;
}

As suggested above, elements of a 1D array are accessed by giving the array name, then [, then a value serving as index, and finally a ]. For example, the code on the right, sets every element of the array x to 34.

Note the use of .length.

6.2.5: Array Initializers

Creating an integer array containing the values 1, 8, 29, and -2 is logically a three step procedure.

  1. Declare the array.
  2. Create the array with room for 4 elements.
  3. Place the given 4 values into the 4 slots.

int[] A;
A = new int[4];

int[] A = new int[4];
int[] A; A = new int[4]; A[0]=1; A[1]=8; A[2]=29; A[3]=-2;
int[] A = {1,8,29,-2};

We have already seen how the first two steps can be combined into one java statement. For example see the first two sections of code on the right.

In fact the Java programmer can write one statement that combines all three steps. The third code group on the right can be written much more succinctly as shown on the fourth group.

Note that the usage of curly brackets as shown is restricted to initialization and a few other situations, you cannot use {1,8,29,-2} in other places.

6.2.6: Processing Arrays

public class Test {
  public static void main(String[] args) {
    int[] a = new int[10];
    int[] b = new int[10];
    int[] c = new int[10];
    for (int i=0; i<a.length; i++) {
      b[i] = 2*i;
      c[i] = i+100;
      a[i] = b[i] + c[i];   // 3i+100
      System.out.printf("a[%d]=%d b[%d]=%2d c[%d]=%d\n",
                        i, a[i], i, b[i], i, c[i]);
    }
  }
}

Very often arrays are used with loops, one iteration of the loop corresponding to one entry in the array.

In particular for loops are often used since they are the most convenient for stepping through a range of values. The first entry of the array is always index 0 and the last entry of an array A is A.length-1.

For example the simple code on the right steps through three arrays, setting the value of two of them and calculating from these values the value of the third.

6.2.7: For-each Loops

We have seen several short-cuts provided by Java for common situations. Here is another. If you want to loop through an array accessing (but not changing) the ith element during the ith iteration, you can use the for-each variant of a for loop.

for(int i=0; i<a.length; i++) {   |  for(double x: a) {
  use (NOT modify) a[i]           |    use (NOT modify) x
}                                 |  }

Assume that x has been defined as an array of doubles. Then instead of the standard for loop on the near right, we can use the shortened, so-called for-each variant on the far right.

6.3: Problem Lotto Numbers

import java.util.Scanner;
public class StdDev {
  public static void main(String[] args) {
    Scanner getInput = new Scanner(System.in);
    System.out.println("How many numbers do you have?");
    final int n = getInput.nextInt();
    System.out.printf("Enter the %d numbers\n", n);
    double[] x = new double[n];
    double sum=0;
    for (int i=0; i<n; i++) {
      x[i] = getInput.nextDouble();
      sum += x[i];
    }
    double mean = sum / n;
    double diffSquared=0;   // std dev is sqrt(diffSquared/n)
    for (double z : x) {
      diffSquared += Math.pow(mean-z,2);
    }
    System.out.printf("Mean is %f; standard deviation is %f\n",
		      mean, Math.sqrt(diffSquared/n));
  }
}

Read

6.3 (Alternate): The Mean and the Standard Deviation

Given n values, x0, ..., xn-1, the mean (often called average and normally written as μ) is the sum of the values divided by n.

The variance is the average of the squared deviations from the mean.
That is, [ (x0-μ)2 + ... + (xn-1)2 ] / n

A Java program to calculate these two values is on the right.

Note that the first loop, which modifies the x array, must use the conventional for loop; whereas, the second loop, which only uses the x array can use the for-each variant.

6.4: Problem: Deck of Cards

public class DeckOfCards {
  public static void main(String[] args) {
    int[] deck = new int[52];
    String[] suits = {"Spades", "Hearts", "Diamonds",
                      "Clubs"};
    String[] ranks = {"Ace", "2", "3", "4", "5", "6",
      "7", "8", "9", "10", "Jack", "Queen", "King"};
    // Initialize cards
    for (int i = 0; i < deck.length; i++)
      deck[i] = i;
    // Shuffle the cards
    for (int i = 0; i < deck.length; i++) {
      // Generate an index randomly
      int index = (int)(Math.random() * deck.length);
      int temp = deck[i];
      deck[i] = deck[index]; 
      deck[index] = temp;
    }
    // Display the shuffled deck
    for (int i = 0; i < 4; i++) {
      String suit = suits[deck[i] / 13];
      String rank = ranks[deck[i] % 13];
      System.out.println("Card number " + deck[i]
         + ": " + rank + " of " + suit);
    }
  }
}

Let's look at this example from the book with some care. I downloaded it from the companion web site. You can download all the code examples.

The idea is to have a deck of cards represented by an array of 52 integers, the ith entry of the array represents the ith card in the deck.

How does an integer represent a card?
First of all the integers are themselves between 0 and 51. Given an integer C (for card), we divide C by 13 and look at the quotient and remainder. The quotient (from 0..4) represents the suit of the card and the remainder (from 0..12) represents the rank.

Look at the String arrays; they give us a way to print the name of the card given its integer value (by using the quotient and remainder).

The deck initialized to be in order starting wit the Ace of Spades and proceeding down to the 2 of Clubs (Liang had Clubs and Diamonds reversed).

Notice how the deck is shuffled: The card in deck[i] is swapped with one from a random location. This can move a single card multiple times. Notice the 3 statement sequence used to swap two values.

Liang printed the first 4 cards; I print the entire deck. The last loop could be written as a one-liner.

for (int C : deck)
  System.out.printf("Card number %2d: %s of %s\n", C, ranks[C%13], suits[C/13]);
  

6.5: Copying Arrays

array1

Serious business. We can no longer put off learning about references.

Consider the situation on the near right. We have created two arrays each holding 6 integers. The first has been initialized, the second has not. The goal is to make the second a copy of the first.

Note that the array name is not the array itself. Instead it points to (technically, refers to) the contents of the array. One consequence is that the size of Array1 does not depend on the number of elements in the array, which turns out to be quite helpful later on.

If we simply execute Array1 = Array2;, we get the situation in the upper right. Both arrays refer to the same content. In this state, changing the contents of one, changes the other.

If the goal is to get the situation in the lower right, you need a loop to copy each entry (or use some built in method that itself has a loop).

6.5.A: Why Didn't This Happen Before???

int a, b;
a = 5;
b = a;
b = 10;

Look at the simple code on the right. According to the picture with arrays, after we execute b=a;, both a and b will refer to the same value, namely 5. Then when we execute b=10;, both a and b will refer to the same value, namely 10.

But this does NOT happen. Instead, b==10, but a==5 as we expected.

Why? val-vs-ref

The technical answer is that primitive types, for example int, have value semantics; whereas, arrays (and objects in general) have reference semantics.

Looking at the diagram on the right we see that the array Array1 refers to (or points at) the actual array where the values reside; whereas, the int a and the int b ARE the values 5 and 10. There are no pointers or references involved.

When you change a or b, you change the value; when you change Array1, you change the reference (pointer).

6.6: Passing Arrays to Methods

Liang first talks about passing the entire array and then about passing individual members. I will reverse the order since I believe passing individual members is easier.

6.6.1: Passing Array Arguments

There is very little to say about passing an element of a one dimensional array as an argument. The element is simply a value of the base type of the array.

public static void printSum(int x, int y) {
  System.out.println(x+y);
}

On the right is the simple printSum method that we saw earlier. It has two integer parameters. If our main method has a declaration int[] A=new int[10], then we can write, printSum(A[4],A[1]). The printSum() method will add the two values and print the sum just as if it was invoked via printSum(8,2);

6.6.A: Passing an Entire Array as an Argument

public static void printArray(int[] A) {
  for (i : A) {
    System.out.println(i);
  }
}

The printArray() method on the right has an (entire) int array as parameter. To call the array is easy: printArray(B) where B is a single-dimensional array of integers. Note that printArray can handle any size array.

The tricky part is the melding of pass by value method invocation and reference semantics for arrays, which we address next.

array-param

Assume we have a method, such as printArray that has an array parameter A and that we call it with an array argument named B.

At the point of the method call the value in B is copied to A, but, as we know, when the method returns any new value in A is NOT copied back to B. This rule still holds for arrays; no change.

The diagram at the right show the configuration when the method is called. Even, if the method changes the value in A, the value in B will not change. It will remain a pointer to the 6 element structure shown.

But now consider what happens if the called method executes A[0]+=1;. This changes not the value in B (a pointer) but instead changes the value in a cell pointed to by B. Hence when the method returns, assuming nothing else happened, the value in B[0] has been increased.

Summary, the call above cannot change the value B, but it can change the value of an individual B[i].

Java also has anonymous arrays, i.e., arrays without a name. So you can write printArray(new int[]{1,5,9});