List partitioning implemented recursively

百般思念 提交于 2019-12-28 19:17:44

问题


I am a Haskell beginner and I have been experimenting with recursive functions.

I am working on a function:

 separate :: [a] -> [[[a]]]

that takes in a list and outputs all of the partitions of that list.

For example 123 becomes:

1|2|3
12|3
1|23
13|2
132

I have only been able to implement a recursive function that creates the 1|2|3 variant:

separate' :: [a] -> [[a]]
separate' (r:rs) = [r]:separate' xs

>separate [1,2,3]
[[1],[2],[3]]

I am stuck with trying to create the other variants with recursion.


回答1:


You can think of this function as choosing, for each place in between two list elements, whether to include a split there. So for starters, there should be 2n-1 partitions for an n-element list: you can use that as a quick sanity check on a possible solution.

One good way to model non-determinism is with the list monad (or equivalently with list comprehensions), so let's do it that way.

First, let's write the type and a base case:

separate :: [a] -> [[[a]]]
separate [] = [[]]

There is a single way to separate an empty list: the empty list itself, with no possibility of splits. Easy enough.

Now, given we have one element and a list of remaining elements, one thing we'll need for sure is a list of all the ways to split the remaining elements:

separate :: [a] -> [[[a]]]
separate [] = [[]]
separate (x:xs) = let recur = separate xs
                  in undefined -- TODO

Here's where the interesting stuff starts. As I said, you can view this as choosing, for each item, whether to put a split after it. Two choices means concatenating together two lists, so let's do that:

separate :: [a] -> [[[a]]]
separate [] = [[]]
separate (x:xs) = let recur = separate xs
                      split = undefined -- TODO
                      noSplit = undefined -- TODO
                  in split ++ noSplit

Now, how do we introduce a split after the item x? We do it by, for each partition in recur, adding [x] to the front of it as a new partition:

separate :: [a] -> [[[a]]]
separate [] = [[]]
separate (x:xs) = let recur = separate xs
                      split = do
                        partition <- recur
                        return $ [x] : partition
                      noSplit = undefined -- TODO
                  in split ++ noSplit

What about not splitting? Pretty similar! For each partition in recur, we add x to the front of the first sub-partition:

separate :: [a] -> [[[a]]]
separate [] = [[]]
separate (x:xs) = let recur = separate xs
                      split = do
                        partition <- recur
                        return $ [x] : partition
                      noSplit = do
                        (y:ys) <- recur
                        return $ (x:y):ys
                  in split ++ noSplit

And with that, we're done:

*Temp> separate "123"
[["1","2","3"],["1","23"],["12","3"],["123"]]



回答2:


A right fold solution would be:

import Control.Applicative ((<$>))

separate :: Foldable t => t a -> [[[a]]]
separate = foldr (\i -> concatMap (inc i)) [[]]
    where
    inc i []     = [[[i]]]
    inc i (x:xs) = ((i:x):xs):((x:) <$> inc i xs)

then:

\> separate [1, 2]
[[[1,2]],[[2],[1]]]

\> separate [1, 2, 3]
[[[1,2,3]],[[2,3],[1]],[[1,3],[2]],[[3],[1,2]],[[3],[2],[1]]]


来源:https://stackoverflow.com/questions/35388734/list-partitioning-implemented-recursively

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!