Interactive development environments—IDEs—are an incredibly powerful tools. We will only scratch the surface on Eclipse’s capabilities in this course. Other IDEs, like IntelliJ and VS Code, have equally if not more impressive tools. Programming is a craft, and your editor is one of your most frequently used tools… it pays to know it!

In that vein, we’ll spend recitation this week learning how to use Eclipse’s debugger. A debugger is a special way of running programs that lets you control the pace at which the program runs, pausing it toinspect it and run parts of it step-by-step.

To get warmed up, let’s watch a brief video on Eclipse’s debugger.

With the basics in mind, let’s explore some recursive programs to get a feel for the stack.

Factorial

First, recall Factorial.java.

package cs284;

public class Factorial {
  public static int fact(int n) {
    if (n == 0) {
      return 1;
    }
    
    return n * fact(n-1);
  }
  
  public static int fact2(int n) {
    return n == 0 ? 1 : n * fact2(n-1);
  }
  
  public static void yolo(int n, int m, int q) {
    System.out.println(n);
    yolo(n+1, m-1, q + 2);
  }
  
  public static void main(String[] args) {
    System.out.println(fact(5));
    System.out.println(fact2(5));
    
    //System.out.println(fact(-1));
    try {
      yolo(0, 0, 0);
    } catch (StackOverflowError e) {
      System.out.println("alas, poor Yorick");
    }
  }
}

Try setting breakpoints inside each of fact, fact2, and yolo. Play around! Use the value inspection pane to change what fact does mid-run.

Linked lists

Now, let’s play with our recursive linked lists, from RLL.java.

package cs284;

public class RLL<E> {
  class Node {
    E data;
    Node next;

    public Node(E data, Node next) {
      this.data = data;
      this.next = next;
    }
  }

  private Node head;

  public RLL() {
    head = null;
  }

  public int size() {
    return size(head);
  }

  private int size(Node n) {
    if (n == null) {
      return 0;
    }

    return 1 + size(n.next);
  }

  public E get(int index) {
    return get(head, index);
  }

  private E get(Node n, int index) {
    if (n == null) {
      throw new IndexOutOfBoundsException();
    }

    if (index == 0) {
      return n.data;
    }

    return get(n.next, index - 1);
  }
  
  public void insert(int index, E elt) {
    if (index == 0) {
      head = new Node(elt, head);
      return;
    }
    
    insert(head, index - 1, elt);
  }
  
  private void insert(Node n, int index, E elt) {
    if (n == null) {
      throw new IndexOutOfBoundsException();
    }

    if (index == 0) {
      n.next = new Node(elt, n.next);
      return;
    }

    insert(n.next, index - 1, elt);
  }
}

Write a main method that creates a list of length 5 and accesses each index. Set a breakpoint in get(int) and step into get(Node, int) to watch the recursive function walk through the list. When you access the nth index, how many stack frames are there?