我将从直接递归开始。分解一下,较长列表中的第一个元素有什么可能性?
- 它可以是其中一个分区列表的唯一元素。
- 它可以是具有多个元素的分区列表的一部分。
从您的示例来看,您似乎希望保持原始元素有序,因此每个分区的成员只能是连续的子列表,这使得它更容易一些。
所以我们可以开始
partitions :: [a] -> [[[a]]]
partitions [] = [[]] -- only one partition of an empty list, an empty partition
partitions (x:xs) = [[x]:part | part <- partitions xs] ++ [(x:ys):yss | (ys:yss) <- partitions xs]
产生
*Partitions> partitions [1,2,3,4]
[[[1],[2],[3],[4]],[[1],[2],[3,4]],[[1],[2,3],[4]],[[1],[2,3,4]],[[1,2],[3],[4]],[[1,2],[3,4]],[[1,2,3],[4]],[[1,2,3,4]]]
不是所需的顺序。如果这很重要,我们必须重写。期望的顺序直接连续列出第一个元素的两个选项,所以我们可以写它
partitions (x:xs) = partitions xs >>= \zs -> case zs of
[] -> [[[x]]]
(ys:yss) -> [[x]:ys:yss, (x:ys):yss]
我们需要明确区分空分区(在递归结束时)和非空分区的情况,这在上面是通过绑定到模式隐式完成的(ys:yss)
。这会产生所需的顺序
*Partitions> partitions [1,2,3,4]
[[[1],[2],[3],[4]],[[1,2],[3],[4]],[[1],[2,3],[4]],[[1,2,3],[4]],[[1],[2],[3,4]],[[1,2],[3,4]],[[1],[2,3,4]],[[1,2,3,4]]]
(>>=)
使用list monad的绑定这一事实,flip concatMap
版本
partitions (x:xs) = concatMap insert (partitions xs)
where
insert [] = [[[x]]]
insert (ys:yss) = [[x]:ys:yss, (x:ys):yss]
可能更具可读性。