我是Haskell初学者,我一直在尝试递归函数。
我正在研究一个函数:
separate :: [a] -> [[[a]]]
接收列表并输出该列表的所有分区。
例如,123变为:
1|2|3
12|3
1|23
13|2
132
我只能实现一个创建1|2|3
变体的递归函数:
separate' :: [a] -> [[a]]
separate' (r:rs) = [r]:separate' xs
>separate [1,2,3]
[[1],[2],[3]]
我一直试图用递归创建其他变种。
答案 0 :(得分:3)
您可以将此功能视为选择两个列表元素之间的每个位置,是否在那里包含拆分。因此对于初学者来说,n元素列表应该有2个 n-1 分区:您可以将其用作对可能解决方案的快速健全性检查。
建模非确定性的一个好方法是使用列表monad(或者等同于列表推导),所以让我们这样做。
首先,让我们写出类型和基本情况:
separate :: [a] -> [[[a]]]
separate [] = [[]]
有一种方法可以分隔一个空列表:空列表本身,不存在拆分的可能性。很容易。
现在,鉴于我们有一个元素和剩余元素列表,我们需要确定的一件事是列出所有分割其余元素的方法:
separate :: [a] -> [[[a]]]
separate [] = [[]]
separate (x:xs) = let recur = separate xs
in undefined -- TODO
这是有趣的东西开始的地方。正如我所说,您可以将此视为选择每个项目是否在其后进行拆分。两个选择意味着将两个列表连接在一起,所以让我们这样做:
separate :: [a] -> [[[a]]]
separate [] = [[]]
separate (x:xs) = let recur = separate xs
split = undefined -- TODO
noSplit = undefined -- TODO
in split ++ noSplit
现在,我们如何在项目x
之后引入拆分?我们为recur
中的每个分区执行此操作,将[x]
添加到其前面作为新分区:
separate :: [a] -> [[[a]]]
separate [] = [[]]
separate (x:xs) = let recur = separate xs
split = do
partition <- recur
return $ [x] : partition
noSplit = undefined -- TODO
in split ++ noSplit
不分裂怎么办?非常相似!对于recur
中的每个分区,我们将x
添加到第一个子分区的前面:
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
有了这个,我们就完成了:
*Temp> separate "123"
[["1","2","3"],["1","23"],["12","3"],["123"]]
答案 1 :(得分:0)
正确的折叠解决方案是:
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)
然后:
\> 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]]]