Diagramming List Recursions

Recall that an invocation tree is an abbreviation for a collection of Java Execution Model frames. Each node of the tree shows the arguments and the result of a method invocation; such a node abbreviates a frame in ExecutionLand. As noted in class, a box-and-pointer diagram is an abbreviation for interconnected linked list nodes in ObjectLand.

It is possible to combine invocation trees and box-and-pointer diagrams to summarize the execution of a list manipulation program. For instance, consider the following definition of a list reversal method (which is assumed to be in a subclass of IntListOps, so that it "understands" append()):

public static IntList reverse (IntList L) {
  if (isEmpty(L)) {
    return L;
  } else {
    return append(reverse(tail(L)), 
                   prepend(head(L), empty()));
  }
}

Suppose that L1 is a list whose printed representation is [1,2,3]. Then the execution of the invocation reverse(L1) is summarized by the following diagram:

A boxed element of the form reverse(X):Y is the root node of an invocation tree for the invocation of the reverse() method on list X that returns result Y . In the invocation tree, particular list nodes are denoted by object references, which are lowercase letters in circles. These object references name nodes appearing in the box-and-pointer portion of the diagram. The object references could have instead been denoted by pointers in the diagram, but this could lead to a spaghetti of pointers. We follow the convention that the empty list is always written as a distinct solid circle. (We are not interested in modeling sharing involving the empty list.)

In the above diagram we have decided to treat append() as a "primitive" by not showing nodes for each invocation of append(). However, the nodes created by the prepend()s performed by the hidden append()s are shown in the box-and-pointer diagrams.

An important feature of the box-and-pointer list diagram portion of the picture is that it accurately depicts sharing. In the case of reverse(), the result returned by one call to reverse does not share any list nodes with the results of any other call. As an example of a diagram with more sharing, consider the following method, which is also assumed to be defined in a subclass of the IntListOps class:

public static IntList appendTails (IntList L) {
  if (isEmpty(L)) {
    return L;
  } else {
    return append(L, appendTails(tail(L)))
  }
}

Below is a diagram showing an invocation tree and box-and-pointer list structure for the invocation appendTails(L1), where L1 is defined as above. Note how the final result (the node labelled"f") includes as subparts the results to the recursive calls (the nodes labelled "e" and "d").