Recursive permutations in Haskell

I'm trying to implement Steinhaus-Johnson-Trotter algorithm for generating permutations. My code is below:

permutations :: [a] -> [[a]]
permutations [] = []
permutations (x:[]) = [[x]]
permutations xs = [ys ++ [xs !! i] | i <- [len,len-1..0], ys <- permutations (delete i xs)]
  where len = (length xs)
        delete i xs = take i xs ++ drop (succ i) xs

This is a direct translation from the Python code:

def perms(A):
    if len(A)==1:
        yield A
    for i in xrange(len(A)-1,-1,-1):
        for B in perms(A[:i]+A[i+1:]):
            yield B+A[i:i+1]

Python code works, but Haskell code enters an infinite recursion. permutations (delete i xs) inside the list comprehension should bring the flow closer to base case. Why does infinite recursion happen?

Edit: @augustss says:

Always beware when you have multiple base cases for a function over lists.

So i changed the base case from

permutations [] = []
permutations (x:[]) = [[x]]

to more simple

permutations [] = [[]]

Your loops aren't the same.

i <- [len,len-1..0]

vs

for i in xrange(len(A)-1,-1,-1):

The very first case, you're binding i to the length, not the length minus one. The result is that delete i xs returns xs , so you get infinite recursion.

I also have a couple of side notes.

First !! is linear-time. You'd be much better off writing a helper function that combines that !! , the delete , and the iteration over the input into one list traversal. Something like select :: [a] -> [(a, [a])] . You can do that efficiently.

Second, ++ is also linear time. Using it to append a single element to an existing list is slow. If your goal is just to produce all the permutations, rather than a specific ordering of them, you should probably use (xs !! i) : ys as the expression to return. (Suitably modified for any changes made in response to the first point.)


select

Based on @Carl's answer I implemented select :: [a] -> [(a, [a])] function. It's task is to generate a list of tuples (a, [a]) , where tuple's 1st part is an element from the list, and tuple's 2nd part is all elements from the list except that element.

select :: [a] -> [(a, [a])]
select [] = []
select (x:xs) = select' x [] xs
  where
    select' x left [] = [(x, left)]
    select' x left right@(r:rs) = (x, left++right) : select' r (x:left) rs

However I've found even simpler implementation of select on Haskell Libraries mailing list:

select :: [a] -> [(a,[a])]
select [] = []
select (x:xs) = (x,xs) : [(y,x:ys) | (y,ys) <- select xs]

Keep in mind that these 3 are equivalent ( second is a function from Control.Arrow ):

[(y,x:ys) | (y,ys) <- select xs]
map ((y,ys) -> (y,x:ys)) (select2 xs)
map (second (x:)) (select2 xs)

Here is the example of how to use select :

select [1,2,3] -- [(1,[2,3]),(2,[1,3]),(3,[2,1])]

Before I implemented select , I tried to find functions with type [a] -> [(a, [a])] in Hayoo, there are several implementations in various libraries:

  • as removeEach from utility-ht package
  • as picks from HaskellForMaths package
  • as parts' from hmt-diagrams packages
  • as extractEachElem from rosso package
  • as extractElem from spaceprobe package
  • permutations

    The problem is, our select is not nearly enough to generate all permutations. We can cons both parts of each tuple using uncurry (:) , which has the type (a, [a]) -> [a] , but we only get some permutations, not all:

    map (uncurry (:)) (select [1,2,3]) -- [[1,2,3],[2,1,3],[3,2,1]]
    

    It's clear why, select [1,2,3] creates a list [(1,[2,3]),(2,[1,3]),(3,[2,1])] , but we must permute sublists, which are second parts of each tuple, too! In other words, if we have (1, [2,3]) , we must add (1, [3,2]) too.

    The full code for finding all permutations of a list is below:

    select :: [a] -> [(a,[a])]
    select [] = []
    select (x:xs) = (x,xs) : map ((y,ys) -> (y,x:ys)) (select xs)
    
    permutations :: [a] -> [[a]]
    permutations [] = [[]]
    permutations xs = [cons s2 | s <- select2 xs, s2 <- subpermutations s]
      where cons :: (a, [a]) -> [a]
            cons = uncurry (:)
            subpermutations :: (a, [a]) -> [(a, [a])]
            subpermutations (x,xs) = map (e -> (x, e)) $ permutations xs
    

    Note that the order of permutations of our function will be different from Data.List.permutations . Our function has lexicographic order, while Data.List.permutations hasn't:

    permutations [1,2,3]           -- [[1,2,3],[2,1,3],[2,3,1],[1,3,2],[3,1,2],[3,2,1]]
    Data.List.permutations [1,2,3] -- [[1,2,3],[2,1,3],[3,2,1],[2,3,1],[3,1,2],[1,3,2]]
    

    Finally, if we simplify our permutations function even further, we get the implementation that is at Rosetta Code:

    select :: [a] -> [(a,[a])]
    select [] = []
    select (x:xs) = (x,xs) : map ((y,ys) -> (y,x:ys)) (select xs)
    
    permutations :: [a] -> [[a]]
    permutations [] = [[]]
    permutations xs = [ y:zs | (y,ys) <- select xs, zs <- permutations ys]
    

    Also note that Rosetta Code's implementation that uses insertion-based approach has the same (non-lexicographic) order as Data.List.permutations .

    Notes

    FWIW, there's a function scc :: [(a, [a])] -> [[a]] from package uhc-util , which finds strongly connected components of a Graph. First part of a tuple is a vertex, second part is all vertices, to which the edge from the vertex goes. IOW, graph 1 --> 2 --> 3 becomes [(1, [2]), (2, [3])] .

    scc [(1,[2,3,4]),(2,[1,3,4]),(3,[2,1,4]),(4,[3,2,1])] -- [[3,4], [1,2]]
    
    链接地址: http://www.djcxy.com/p/24320.html

    上一篇: 迭代适合特定键的排列

    下一篇: 在Haskell中递归排列