2

I read this permutation function example in a Haskell textbook and I was wondering if someone could help me understand what is happening. Also, assume that the function delete simply deletes the first occurrence of a value from a list and returns the list

perms [] = [[]]
perms p = [x:xs | x <-p, xs <- perms (delete x p)]

I understand that an empty list equals a list with an empty list. For all other cases the head of the list is prepended to x and the numbers except the head recurses through the algorithm.

My question is how does this work, for example, my pseudocode understanding is

perms[1,2,3]
x <- 1
xs <- [2,3]
perms [2,3]
x <- 2
xs <- 3
perms [3]
x <- 3
xs <- []

this would produce the list [1,2,3] how does the algorithm produce the other list results.

An output of this code working is as follows:

>perms [1,2,3]
[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]] 
1
  • No, p is not the head of the list. You might want to re-read the chapter about list permutations. <- means "for all values in". Commented Mar 14, 2018 at 15:51

1 Answer 1

7

The expression:

[x:xs | x <- p, xs <- perms (delete x p)]

is a list comprehension, which means that each of the a <- b expressions on the right hand side form a sort of implicit loop that your pseudocode doesn't account for.

Written in pseudocode with explicit loops, it's equivalent to:

for each x in p:
  for each xs in perms (delete x p):
    yield (x:xs)
-- and return the list of all results yielded

It can then be helpful to think through the definition recursively from the bottom up. If you run perm [n] for any n, then the outer loop is only evaluated for x = n, and since:

perms (delete n [n]) = perms [] = [[]]

the inner loop is equivalent to:

for each xs in [[]]
  ...

and is only evaluated for xs = []. Therefore, only one value is yielded:

x:xs = n:[] = [n]

So, perm [n] gives a list of the single element [n], in other words [[n]].

If you move on to perm [1,2] and imagine unfolding the loops:

-- for each x in [1,2]
first, for x = 1
  -- for each xs in perms (delete 1 [1,2]) = perms [2] = [[2]]
  so only for xs = [2]
    yield (x:xs) = yield (1:[2]) = yield [1,2]
second, for x = 2
  -- for each xs in perms (delete 2 [1,2]) = perms [1] = [[1]]
  so only for xs = [1]
    yield (x:xs) = yield (2:[1]) = yield [2,1]

so two values are yielded, namely [1,2] and [2,1], giving:

perm [1,2] = [[1,2],[2,1]]

This obviously generalizes to any perm [a,b] = [[a,b],[b,a]], so we can finally calculate perm [1,2,3]:

-- for each x in [1,2,3]
first, for x = 1
  -- for each xs in perms (delete 1 [1,2,3]) = perms [2,3] = [[2,3],[3,2]]
  first for xs = [2,3]
    yield (x:xs) = yield (1:[2,3]) = yield [1,2,3]
  second for xs = [3,2]
    yield (x:xs) = yield [1,3,2]
second, for x = 2
  -- for each xs in perms (delete 2 [1,2,3]) = [[1,3],[3,1]]
  first for xs = [1,3]
    yield (x:xs) = yield [2,1,3]
  second for xs = [3,1]
    yield (x:xs) = yield [2,3,1]
third, for x = 3
  first for xs = [1,2]
    yield [3,1,2]
  second for xs = [2,1]
    yield [3,2,1]

In all, six values are yielded, giving the list:

perms [1,2,3] = [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.