Haskell 递归实现的列表分区

Haskell 递归实现的列表分区,haskell,Haskell,我是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]]

我是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]]

我一直在尝试使用递归创建其他变量。

您可以将此函数看作是为两个列表元素之间的每个位置选择是否包含拆分。因此,对于初学者来说,一个n元素列表应该有2n-1个分区:您可以使用它来快速检查可能的解决方案

对非决定论建模的一个好方法是使用列表单子(或等效于列表理解),所以让我们这样做吧

首先,让我们编写类型和基本情况:

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
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"]]

您可以将此函数看作是为两个列表元素之间的每个位置选择是否包含拆分。因此,对于初学者来说,一个n元素列表应该有2n-1个分区:您可以使用它来快速检查可能的解决方案

对非决定论建模的一个好方法是使用列表单子(或等效于列表理解),所以让我们这样做吧

首先,让我们编写类型和基本情况:

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
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"]]

正确的解决方案是:

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)

正确的解决方案是:

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)

为什么要包括
132
,而不是
123
321
?我认为“分区”意味着输出不应该被重新安排,即
concat(concat(separate x))==x
,因此应该包括
123
,但不包括
132
321
。为什么包括
132
,而不包括
123
321
?我认为“分区”意味着输出不应该被重新安排,也就是说,
concat(concat(separate x))==x
,所以应该包括
123
,而不是
132
321
。感谢您的详细回复,对于初学者来说,这真的很有帮助。不过我有两个问题。noSplit之后“Y”来自哪里?此外,在我用来引用分区列表定义的Wikipedia页面上,它还将[[“13”,“2”]]列为“123”的可能结果。考虑到这一点,要满足这个条件还需要多少实现?这是什么维基百科页面?很难想象一个包括[13,2]但不包括[3,12]的问题陈述,所以我不能说你将如何以一种有意义的方式做出你想要的改变。至于
ys
:在
recur
中的每个项目都是列表的列表,因此当我们将
(y:ys)
绑定到单个项目时,我们让
y
作为第一个列表,而
ys
作为列表的所有其余部分。因此,您基本上是在尝试使用集合,而不是列表。这解释了大家对示例的困惑,因为您专门讨论了列表,但正在寻找基于集合的算法。你可以应用同样的原理,得到一个类似的函数,它可以在集合上工作,但这不仅仅是对我的答案的简单修改:你必须重写它。也许你应该问另一个问题,这更清楚地说明了你想从答案中得到什么。关于
[“13”,“2”]
?@eulerfx呢?你问的问题与其他提到相同价值的评论有什么不同?谢谢你的详细回答,作为初学者,这真的很有帮助。不过我有两个问题。noSplit之后“Y”来自哪里?此外,在我用来引用分区列表定义的Wikipedia页面上,它还将[[“13”,“2”]]列为“123”的可能结果。考虑到这一点,要满足这个条件还需要多少实现?这是什么维基百科页面?很难想象一个包括[13,2]但不包括[3,12]的问题陈述,所以我不能说你将如何以一种有意义的方式做出你想要的改变。至于
ys
:在
recur
中的每个项目都是列表的列表,因此当我们将
(y:ys)
绑定到单个项目时,我们让
y
作为第一个列表,而
ys
作为列表的所有其余部分。因此,您基本上是在尝试使用集合,而不是列表。这解释了大家对示例的困惑,因为您专门讨论了列表,但正在寻找基于集合的算法。您可以应用相同的原理来生成一个在集合上工作的类似函数,但是