Using Lists of Lists -- Permutations Example

Below is one strategy for solving the permutations problem. The code below uses the IntList and IntListList classes which are abbreviated IL and ILL respectively. Studying the code will help you in your understanding of how to use IntLists and IntListLists. P will represent the method which gives us the list of permutations for a given IntList of numbers. The skeleton for P is
public static IntListList P (IntList L)

Solving Permutations using Wishful Thinking

Let's say we want to find the permutations for the IntList L=[1,2,3]. By wishful thinking, we can solve the problem for the tail of L1.
P(tail(L)) =
  P([2,3]) =
    [[2,3],[3,2]]
The question is, how do we combine the head of the list (in this case, 1) to the result we get from finding the permutations of the tail of the list in order to get the final answer? What we need to do is to insert the head of the list into all the spots in each of the lists we get for P(tail(L)). Let's say we have a method called insertNumber which does this for us. This is how insertNumber would work
insertNumber(1,[2,3]) = [[1,2,3],[2,1,3],[2,3,1]]
insertNumber(1,[3,2]) = [[1,3,2],[3,1,2],[3,2,1]]
In other words, given an integer and an IntList, insertNumber returns an IntListList created by placing the integer into the IntList in each of the possible places in the IntList (i.e. at the beginning, between each element, and at the end). Assuming we have insertNumber (more wishful thinking!), the way we can solve our permutations problem is by creating a mapping function which applies insertNumber with the head of our list to the result we get from finding the permutations of the tail of the list. This is how mapInsertNumber would work
mapInsertNumber(1,[[2,3],[3,2]]) =
  ILL.append(insertNumber(1,[2,3]),
              insertNumber(1,[3,2]))
Since insertNumber gives us an IntListList, we put all the results of applying insertNumber to each list in the IntListList by appending them together. So, our code for permutations so far is the following:
// Returns the permutations of a given list of integers.
public static IntListList P (IntList L) {
  if (IL.isEmpty(L)) {
    // can't just return an empty IntListList or else there will be nothing to prepend on to!
    return ILL.prepend(IL.empty(),ILL.empty());
  } else {
    // wishful thinking strategy: insert the head of the list
    // into the answer we get from calculating the permutations of the tail of the list
    return mapInsertNumber(IL.head(L), P(IL.tail(L)));
  }
}

// Returns an IntListList from applying insertNumber of the given integer n
// to each IntList in the given IntListList L
public static IntListList mapInsertNumber (int n, IntListList L) {
  if (ILL.isEmpty(L)) {
    return ILL.empty();
  } else {
    return ILL.append(insertNumber(n, ILL.head(L)),
                       mapInsertNumber(n, ILL.tail(L)));
  }
}

Solving insertNumber using Wishful Thinking

Now we need to figure out how to insert an integer into all the available spots in an IntList. The skeleton for insertNumber is
public static IntListList insertNumber (int n, IntList L)
Let's use the following as our concrete example:
insertNumber(1,[2,3,4]) =
  [[1,2,3,4],[2,1,3,4],[2,3,1,4],[2,3,4,1]]
Using wishful thinking, let's assume that we can find the result for applying insertNumber to the tail of the list.
insertNumber(1,[3,4]) =
  [[1,3,4],[3,1,4],[3,4,1]]
What do we need to do to the answer to our smaller problem in order to get the answer to our original problem? There are two things that need to be done. We need to get the solution which starts with the integer given. It's the one we get if we add n to the beginning of L which we can do with the following bit of code:
IL.prepend(n,L)
The rest of the solutions don't begin with n. What do they begin with? The head of L! In fact, all we need to do is prepend the head of L to each list we get from the result of doing insertNumber(n,tail(L)). This is a mapping function so we will call it mapPrepend. The way mapPrepend works is that it takes an integer and a list of IntLists and it returns the IntListList which results from prepending the integer to each IntList inside the given IntListList. So, mapPrepend works like so:
mapPrepend(2,[[1,3,4],[3,1,4],[3,4,1]]) =
  ILL.prepend(IL.prepend(2,[1,3,4]),
    ILL.prepend(IL.prepend(2,[3,1,4]),
      ILL.prepend(IL.prepend(2,[3,4,1]),
        ILL.empty())))

So, our code for the rest of the problem is the following:

// Returns the list of lists which result from inserting a given integer n
// into all the possible places in a given IntList L.
public static IntListList insertNumber (int n, IntList L) {
  if (IL.isEmpty(L)) {
    // if given an empty IntList, we want to create an IntList with n in it
    // and that will be the single element in the resulting IntListList
    return ILL.prepend(IL.prepend(n, IL.empty()),
                       ILL.empty());
  } else { // implementation of strategy outlined above
    return ILL.prepend(IL.prepend(n, L), // solution with n at the beginning
                       // solutions starting with the head of the list given
                       mapPrepend(IL.head(L), insertNumber(n, IL.tail(L))));
  }
}

// Returns an IntListList which has the integer n prepended to each
// IntList in L.
public static IntListList mapPrepend (int n, IntListList L) {
  if (ILL.isEmpty(L)) { // no lists to prepend an integer to
    return L;
  } else {
    return ILL.prepend(IL.prepend(n, ILL.head(L)), // prepend to first list
                       mapPrepend(n, ILL.tail(L))); // prepend to rest of lists
  }
}