#### Lecture 7: Accumulator methods, continued

Methods on trees, and accumulator-style methods on lists

##### Overview

We continue with our ancestry trees example from Lecture 6, working through two subtle examples with accumulators, and then ending with a brief discussion of how accumulators can behave in potentially unexpected ways.

##### 7.1` `Finding the younger of two IATs

// In IAT: // To return the younger of this ancestor tree and the given ancestor tree IAT youngerIAT(IAT other);

If this IAT is Unknown, and the given IAT is Unknown, return Unknown.

If this IAT is Unknown, and the given IAT is a Person, return the Person.

If this IAT is a Person, and the given IAT is Unknown, return the Person.

If this IAT is a Person, and the given IAT is Person, return the younger of the two Persons.

// In Unknown: // To return the younger of this Unknown and the given ancestor tree public IAT youngerIAT(IAT other) { return other; }

// In Person: // To return the younger of this Person and the given ancestor tree public IAT youngerIAT(IAT other) { /* Template * Fields: * this.yob -- int * ... others as before * Methods: * this.youngerIAT(IAT) -- IAT * ... others as before * Methods on fields * ... others as before * Parameters: * other -- IAT * Methods on parameters * other.youngerIAT(IAT) -- IAT */ }

// In IAT: // To return the younger of this ancestor tree and the given ancestor tree IAT youngerIAT(IAT other); // To return either this ancestor tree (if this ancestor tree is younger // than the given yob) or the given ancestry tree IAT youngerIATHelp(IAT other, int otherYob);

// In Unknown: public IAT youngerIAT(IAT other) { return other; } // To return either this Unknown (if this Unknown is younger than the // given yob) or the given ancestry tree IAT youngerIATHelp(IAT other, int otherYob) { return other; }

// In Person: // To return the younger of this Person and the given ancestor tree public IAT youngerIAT(IAT other) { /* Template * Fields: * this.yob -- int * ... others as before * Methods: * this.youngerIAT(IAT) -- IAT * this.youngerIATHelp(IAT, int) -- IAT * ... others as before * Methods on fields * ... others as before * Parameters * other -- IAT * Methods on parameters * other.youngerIAT(IAT) -- IAT * other.youngerIATHelp(IAT, int) -- IAT */ return other.youngerIATHelp(this, this.yob); } // To return either this Person (if this Person is younger than the // given yob) or the given ancestry tree IAT youngerIATHelp(IAT other, int otherYob) { /* same template as above */ if (this.yob > otherYob) { return this; } else { return other; } }

Do Now!

Why should we pass both this and this.yob? And why should invoking a method on other be of any help here?

(Look at the purpose statement for other.youngerIATHelp very carefully, substituting “this” and “other” appropriately: If other is an Unknown, then other.youngerIATHelp(this, this.yob) will return this Person. On the other hand, if other is another Person, then other.youngerIATHelp(this. this.yob) will return other if other is younger than this Person, or else this Person.)

##### 7.2` `Finding the youngest grandparent

To determine the youngest grandparent of a given IAT, let’s try a simpler method first: determine the youngest parent of a given IAT.

// In IAT: // To compute the youngest parent of this ancestry tree IAT youngestParent();

// In Unknown: // To compute the youngest parent of this Unknown public IAT youngestParent() { return new Unknown(); }

// In Person: // To compute the youngest parent of this Person public IAT youngestParent() { /* Template: * Fields: * this.mom -- IAT * this.dad -- IAT * ... others as before * Methods: * this.youngestParent() -- IAT * this.youngerIAT(IAT other) --- IAT * this.youngerIATHelp(IAT other, int otherYob) --- IAT * Methods of fields: * this.mom.youngestParent() -- IAT * this.mom.youngerIAT(IAT other) --- IAT * this.mom.youngerIATHelp(IAT other, int otherYob) --- IAT * this.dad.youngestParent() -- IAT * this.dad.youngerIAT(IAT other) --- IAT * this.dad.youngerIATHelp(IAT other, int otherYob) --- IAT */ }

// In Person: // To compute the youngest parent of this Person public IAT youngestParent() { return this.mom.youngerIAT(this.dad); }

// In IAT: // To compute the youngest grandparent of this ancestry tree IAT youngestGrandparent();

// In Unknown: // To compute the youngest grandparent of this Unknown public IAT youngestGrandparent() { return new Unknown(); }

// In Person: // To compute the youngest grandparent of this Person public IAT youngestGrandparent() { /* Template: * Fields: * this.mom -- IAT * this.dad -- IAT * ... others as before * Methods: * this.youngestParent() -- IAT * this.youngestGrandparent() -- IAT * this.youngerIAT(IAT other) --- IAT * this.youngerIATHelp(IAT other, int otherYob) --- IAT * Methods of fields: * this.mom.youngestParent() -- IAT * this.mom.youngestGrandparent() -- IAT * this.mom.youngerIAT(IAT other) --- IAT * this.mom.youngerIATHelp(IAT other, int otherYob) --- IAT * this.dad.youngestParent() -- IAT * this.dad.youngestGrandparent() -- IAT * this.dad.youngerIAT(IAT other) --- IAT * this.dad.youngerIATHelp(IAT other, int otherYob) --- IAT */ return this.mom.youngestParent().youngerIAT(this.dad.youngestParent()); }

How can we generalize this to greatgrandparents and beyond? Notice that the implementations of youngestParent and youngestGrandparent are pretty similar: they both invoke youngerIAT on the youngest appropriate relative on this.mom’s side and the youngest appropriate relative on this.dad’s side.

// this.andrew.youngestAncInGen(1) ==> this.andrew.youngestParent() // this.andrew.youngestAncInGen(2) ==> this.andrew.youngestGrandparent() // this.andrew.youngestAncInGen(3) ==> this.andrew.youngestGreatGrandparent() // this.andrew.youngestAncInGen(0) ==> ?

// In IAT: // To compute the youngest ancestor in the given generation of this ancestry tree IAT youngestAncInGen(int gen);

// In Unknown: // To compute the youngest ancestor in the given generation of this Unknown public IAT youngestAncInGen(int gen) { if (gen == 0) { return this; } else { return new Unknown(); } }

// In Person: // To compute the youngest ancestor in the given generation of this Person public IAT youngestAncInGen(int gen) { /* Template: * Fields: * this.mom -- IAT * this.dad -- IAT * ... others as before * Methods: * this.youngestAncInGen(int gen) -- IAT * this.youngerIAT(IAT other) --- IAT * this.youngerIATHelp(IAT other, int otherYob) --- IAT * Methods of fields: * this.mom.youngestAncInGen(int gen) -- IAT * this.mom.youngerIAT(IAT other) --- IAT * this.mom.youngerIATHelp(IAT other, int otherYob) --- IAT * this.dad.youngestAncInGen(int gen) -- IAT * this.dad.youngerIAT(IAT other) --- IAT * this.dad.youngerIATHelp(IAT other, int otherYob) --- IAT * Parameters: * gen -- int */ if (gen == 0) { return this; } else { return this.mom.youngestAncInGen(gen - 1).youngerIAT(this.dad.youngestAncInGen(gen - 1)); } }

Do Now!

Complete the definition of youngestGrandparent, now that youngestAncInGen is defined.

The gen parameter is our accumulator, where this time it is “counting down” to decide at what depth to stop the recursion.

##### 7.3` `Potential hazards of accumulator-style methods

Exercise

Design a method append for lists of Strings twice: first in direct style, and then again using an accumulator parameter.Do you notice any differences in the output? (Did you write enough tests?)