Student: Professor, I want to get the hard stuff. The real stuff about programming, the can’t-fake-it, no-nonsense big ideas. Stuff like recursion. My friends in upper division classes are always talking about that.
Prof: Good news! We’ve done recursion already.
Student: What?
Prof: We wrote our first recursive methods the last time we talked. And we’ve been making recursive data since we composed the French flag.
Student: How do you mean? Recursion is the hardest topic in CSE11. That’s what everyone says.
Prof: Then you’re doing great, as long as you understood our last conversations!
Student: Which parts?
Prof: Well, let’s bring up an example again and talk through a definition.
Prof: So, in terms of definitions:
- The
Andrecord is a recursive datatype: it both implementsImageQueryand it has a field of typeImageQuery. That means a field of anAndrecord can itself be an instance ofAnd. - The
matchesmethod is a recursive method: when it callsmatcheson its fields, it may be calling the exact method it is in. That is, two stack frames right next to each other, each for the exact same method of the exact same class, just different receivers.
These are the two important definitions of recursion in the object-oriented programs we will write.
Student: Huh. OK. So the memory trace would have multiple stack frames for the
matches method on And at the same time?Prof: Right, that’s the idea:
Names (the stack), over time →
top level
img = @1001cat = @1002png = @1003jpg = @1004light = @1005pngOrJpg = @1006catPic = @1007query = @1008found = truequery.matches(img) on @1008img = @1001returns trueleft.matches(img) on @1007img = @1001returns trueleft.matches(img) on @1002img = @1001returns trueright.matches(img) on @1006img = @1001returns trueleft.matches(img) on @1003img = @1001returns trueright.matches(img) on @1005img = @1001returns trueHeap
@1001: ImageData[keywords=cat sleeping, fileType=png, width=640, height=480, sizeKb=300]@1002: HasKeyword[word=cat]@1003: HasExtension[ext=png]@1004: HasExtension[ext=jpg]@1005: SmallerThanKb[limitKb=500]@1006: Or[left=@1003, right=@1004]@1007: And[left=@1002, right=@1006]@1008: And[left=@1007, right=@1005]Student: Hm, ok so recursive data is when a record could have other instances of the same record in its fields? And recursive methods are when a method can call itself?
Prof: That’s right.
Practice
Beside from the Pict library is recursive data too: its left and right are Picts, and its width method calls width on both of them.
record Beside(Pict left, Pict right) implements Pict {
public int width() { return left.width() + right.width(); }
// ...
}
Pict dot = new Circle(10, "red");
Pict box = new Rect(30, 20, "blue");
Pict pair = new Beside(dot, box);
Pict row = new Beside(pair, dot);
int w = row.width();These are the calls of row.width() over time, with the receivers left out.
Names (the stack), over time →
top level
dot = @1001box = @1002pair = @1003row = @1004w = 70row.width() on @1004returns 70left.width() on ???returns 50left.width() on ???returns 20right.width() on ???returns 30right.width() on ???returns 20Heap
@1001: Circle[radius=10, color=red]@1002: Rect[width=30, height=20, color=blue]@1003: Beside[left=@1001, right=@1002]@1004: Beside[left=@1003, right=@1001]Fill in the receiver of each call.
The call that returns
The call that returns
Both calls that return
50 runs on .The call that returns
30 runs on .Both calls that return
20 run on .Which two calls are next to each other on the stack, running the same method of the same record?
row.width()and thethis.left.width()that returns50Both runwidthfromBeside: one on@1004, and the one it calls on@1003.- the
this.left.width()that returns20and thethis.right.width()that returns30@1001is aCircleand@1002is aRect, so these are two differentwidthmethods, and neither one called the other. - the two calls that return
20These run on the same object,@1001, but neither one called the other. They are never on the stack at the same time.