Permutation implementation in Haskell

I was trying to implement permutation of a list in Haskell. The idea for permutations is this:

The base cases are when list length is 0 and 1 which is the list itself, and when size is 2, the permutation gives the list itself along with swapped elements.

Now, given a list [a,b,c,d] we permute [b,c,d] and append with a. and we change our list to have b in the first like [b,a,c,d] and permute [a,c,d] and so on, recursively.

So far, I have done the following code in Haskell. Which perfectly works. But I am not satisfied with the level of 'haskell-ness' that this contains. I would like to have some hints on how to make this more readable and efficient in haskell. Thanks in advance. The code is below:

-- swap the first element of a list with the element at the index
swapFirstWith index l | index == 0 = l
                      | otherwise =  [l!!index]
                        ++ (take (index-1) (tail l))
                        ++ [head l]
                        ++ (drop (index+1) l)


permutations :: [a] -> [[a]]
permutations [] = [[]]
permutations [a] = [[a]]
permutations [a,b] = [[a,b], [b,a]]
permutations lst = foldl (++) [] (map (x-> miniperms x) swappedList)
    where miniperms l = map (x-> (head l): x) $ permutations (tail l)
          swappedList = map ((i, x) -> swapFirstWith i lst) (zip [0..] lst)


main = do
    putStrLn $ show $ perms
    putStrLn $ show $ length perms
    where lst = [1,2,3,4,5,6,7,8,9]
          perms =  permutations lst

Avoid !!,head,tail in favor of pattern matching. Such functions are partial, and can crash your program when the list is too short. Pattern matching (when exhaustive) has no such issues.

length, take, drop are often better left unused.

Instead, let us consider the simple recursion:

perms :: [a] -> [[a]]
perms []     = [[]]
perms (x:xs) = doSomething x (perms xs)

How to turn perms xs into perms (x:xs) ? In each permutation p of xs , we need to insert x at any possible point of p . We get

perms :: [a] -> [[a]]
perms []     = [[]]
perms (x:xs) = [ p' | p <- perms xs, (use insert here) ]

where inserting at all points is done as follows

insert :: a -> [a] -> [[a]]
insert x [] = [[x]]
insert x (y:ys) = ... -- todo

I'll leave to you to complete the code.


With

picks :: [t] -> [([t], t)]
picks [] = []
-- picks [x] = [([],x)]
picks (x:xs) = [(xs,x)] ++ [(x:ys,y) | (ys,y) <- picks xs]

it is, straightforwardly,

perms :: [t] -> [[t]]
perms [] = [[]]
perms xs =         -- [(x:zs) | (ys,x) <- picks xs, zs <- perms ys]  
  do                     
    (ys,x) <- picks xs        -- pick an element, any element
    zs     <- perms ys        -- permute what's left
    return (x:zs)             -- and put them together     

edit: The repetitive pattern of creating and passing around the updated domain suggests that we can do better, ie make it so that the correct domains are passed around automatically behind the scenes for us, as a part of this specific computational model's "pipeline", as it were.

Right now we have to worry about making a mistake, to name the interim domains explicitly, and to be extra careful to pass the correct variable around as the domain to be used. It's good to have these worries taken care of automatically for us.

Specific notions of computation are captured with a specific instance of a Monad type class.

With the help of "unique selection" monad from an answer by Louis Wasserman,

newtype UniqueSel t a = UniqueSel {runUS :: [t]  ->  [ ([t],  a) ] }
--                                       domain   updated_dom, result
instance Functor (UniqueSel t) where
    fmap = liftM
instance Applicative (UniqueSel t) where
    pure a = UniqueSel ( choices -> [(choices, a)])    -- unchanged domain
    (<*>)  = ap 
instance Monad (UniqueSel t) where
    return  = pure
    m >>= k = UniqueSel ( choices -> [ r | (cs, a) <- runUS m choices,
                                            r       <- runUS (k a) cs ])

we could re-write the above list-based do code as UniqueSel -based do code,

perm = do { x <- UniqueSel picks ; xs <- perm ; return (x:xs) }

where all the interim domain tracking variables have just disappeared! The nature of what we're doing here has become much clearer and more apparent. There's no distractions anymore.

This last code snippet won't work though, as we don't guard against making a selection from an empty domain, which will happen and will effectively abort all computations, producing just [] in the end. We need to return an [] as the result for the empty domains, ourselves.

We could introduce new "primitive" action in our little uniquely-selecting computations language, to bring the hidden choices into our universe, with choices = UniqueSel (cs -> [(cs, cs)]) ; and branch on the empty domain, like

perm = do { cs <- choices ; if (null cs) then return [] else
            do {  x <- UniqueSel picks ; xs <- perm ; return (x:xs) } }

and run this computation description that we've built, by using perms = map snd . runUS perm perms = map snd . runUS perm ; but this pattern is already captured for us in the standard library, in the module Control.Monad , as the function sequence ; so we can just define

perms :: [t] -> [[t]]
perms = map snd . (runUS =<< sequence . (UniqueSel picks <$))

-- perms xs = map snd $ runUs (sequence [UniqueSel picks | _ <- xs]) xs
--          = .....           (replicateM (length xs) (UniqueSel picks)) xs

This runs the input through the sequence of picks of the same length as the input.

Indeed, to permute an n -long list is to make n arbitrary selections from the ever shrinking pool of possible choices.

链接地址: http://www.djcxy.com/p/24326.html

上一篇: Grails GORM和MYSQL级联删除问题

下一篇: Haskell中的排列实现