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, i.e. 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
; 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.