Welcome

Bailey chapter 1, on objects generally.

Bailey chapter 2, on assertions. We will come back to this later; you can skim it for now.

Bailey chapter 3, on a Vector class

Morin chapter 1, sections 1.1 and 1.2

One slight peculiarity of Morin is that he refers to the array-based List implementation of chapter 2 as an Array

Primary text: Bailey, online, and maybe Morin, also online.

expand() in C++

public void add(String y) {

if (currsize ==
elements.length) {

System.out.println("no more room
to add " + y); // fix this to increase space
instead!

expand(); // postcondition:
elements.length is greater than it was!

}

elements[currsize] = y;

currsize +=
1; // or currsize++

}

// Java

void expand() {

??? newelements = ???

// copy from elements into newelements

elements = newelements;
// update pointer

}

// C++

void expand() {

??? newelements = ???

// copy from elements into newelements

** delete[] elements; **
// where does it
go?

elements = newelements;
// update pointer

}

A simple example of a precondition is that the function Math.sqrt(double x) requires that x>=0. The postcondition is something that is true afterwards,on the assumption that the precondition held (in this case, that the value returned is a "good" floating-point approximation to the squareroot of x). Note that sometimes precondition X is replaced in Java with the statement that "an exception is thrown if X is false"; this is probably best thought of as amounting to the same thing.

Note that it is up to the caller of a function to verify the precondition. Sometimes (though not always) the function verifies that the preconditions hold.

An invariant is a statement that is both pre and post: if it holds at the start, then it still holds at the end. The classic example is a

int sum = 0;

int n=0;

while (n<=100) { // invariant: sum = 1+2+...+n

n += 1;

sum += n;

}

We're not going to obsess about these, but they're good to be familiar with. Most loop invariants are either not helpful or are hard to write down; sometimes, however, they can really help clear up what is going on.

Consider again the Ratio class. One version of the gcd() method was recursive: it calls itself. But we also had an iterative version:

// pre: a>=0,
b>=0

int gcd(int a, int b) {

while (a>0 && b>0) {

if (a>=b) a = a % b;

else b = b % a;

}

if (a==0) return b; else return a;

int gcd(int a, int b) {

while (a>0 && b>0) {

if (a>=b) a = a % b;

else b = b % a;

}

if (a==0) return b; else return a;

Is there an invariant we can use here? Basically, the gcd of a and b never changes. How do we write that?

How do we write the invariant? First, we note that gcd(a,b) = gcd(a,b%a), always; any divisor of a and b is a divisor of b%a (which has the form b-ka), and any divisor of a and b%a is a divisor of b. So, when rgcd(a,b) returns rgcd(a,b%a), that is the same value, by invariance.

Second, though, there's the question of whether rgcd() ever *returns*.
One way to prove this is to argue that the first parameter to rgcd() keeps
getting smaller. We stop when it reaches 0, as it must. The **atomic
case** in the recursion is the case that involves no further
recursive calls; in the gcd() example it is the case when a==0.

When we're dealing with loops we also should argue that the loop terminates. Usually this "seems" more obvious.

List-related examples:

So far we've been looking mainly at running time. We can also consider space needs.

See recursion.html