I am studying the code in the Data.List module and can\'t exactly wrap my head around this implementation of permutations:
permutations :: [a] -&g
The basic algorithm is based on the idea of taking one item from the list at a time, finding every permutation of items including that new one, and then repeating.
To explain what this looks like, [1..] will mean a list from one up, where no values (no even the first) have been examined yet. It is the parameter to the function. The resulting list is something like:
[[1..]] ++
[[2,1,3..]] ++
[[3,2,1,4..], [2,3,1,4..]] ++ [[3,1,2,4..], [1,3,2,4..]]
[[4,3,2,1,5..], etc
The clustering above reflects the core idea of the algorithm... each row represents a new item taken from the input list, and added to the set of items that are being permuted. Furthermore, it is recursive... on each new row, it takes all the existing permutations, and places the item in each place it hasn't been yet (all the places other then the last one). So, on the third row, we have the two permutations [2,1] and [1,2], and then we take place 3 in both available slots, so [[3,2,1], [2,3,1]] and [[3,1,2], [1,3,2]] respectively, and then append whatever the unobserved part is.
Hopefully, this at least clarifies the algorithm a little. However, there are some optimizations and implementation details to explain.
(Side note: There are two central performance optimizations that are used: first, if you want to repeatedly prepend some items to multiple lists, map (x:y:z:) list is a lot faster then matching some conditional or pattern matching, because it has not branch, just a calculated jump. Second, and this one is used a lot, it is cheap (and handy) to build lists from the back to the front, by repeatedly prepending items; this is used in a few places.
The first thing the function does is establish a two bases cases: first, every list has one permutation at least: itself. This can be returned with no evaluation whatsoever. This could be thought of as the "take 0" case.
The outer loop is the part that looks like the following:
perms (t:ts) is = (perms ts (t:is))
ts is the "untouched" part of the list, that we are not yet permuting and haven't even examined yet, and is initially the entire input sequence.
t is the new item we will be sticking in between the permutations.
is is the list of items that we will permute, and then place t in between, and is initially empty.
Each time we calculate one of the above rows, we reach the end of the items we have prepended to the thunk containing (perms ts (t:is)) and will recurse.
The second loop in is a foldr. It for each permutation of is (the stuff before the current item in the original list), it interleaves the item into that list, and prepends it to the thunk.
foldr interleave (permutations is)
The third loop is one of the most complex. We know that it prepends each possible interspersing of our target item t in a permutation, followed by the unobserved tail onto the result sequence. It does this with a recursive call, where it folds the permutation into a stack of functions as it recurses, and then as it returns, it executes what amounts to a two little state machines to build the results.
Lets look at an example: interleave [ where t = 4 and is = [5..]
First, as interleave' is called recursively, it builds up ys and fs on the stack, like this:
y = 1, f = id
y = 2, f = (id . (1:))
y = 3, f = ((id . (1:)) . (2:))
(the functions are conceptually the same as ([]++), ([1]++), and ([1,2]++) respectively)
Then, as we go back up, we return and evalute a tuple containing two values, (us, zs).
us is the list to which we prepend the ys after our target t.
zs is the result accumulator, where each time we get a new permutation, we prepend it to the results lists.
Thus, to finish the example, f (t:y:us) gets evaluated and returned as a result for each level of the stack above.
([1,2]++) (4:3:[5..]) === [1,2,4,3,5..]
([1]++) (4:2[3,5..]) === [1,4,2,3,5..]
([]++) (4:1[2,3,5..]) === [4,1,2,3,5..]
Hopefully that helps, or at least supplements the material linked in the author's comment above.
(Thanks to dfeuer for bringing this up on IRC and discussing it for a few hours)