列表理解:制作列表列表

时间:2010-11-16 17:31:45

标签: haskell list-comprehension

我试图在haskell中创建一个带有数字a的函数,使用列表对其进行分配,即对于数字4,它将创建[[1,1,1,1],[1,1,2],[1,3],[2,2],[4]]。我正在考虑使用列表理解,它会创建列表x,然后使用[1 ... n]中的数字创建更多列表(n是我想要的分区号),其中创建的列表总和相等到n。

到目前为止我创建的代码是 -

partions (n:xs) = [[x|x<-[1...n], sum[x]==n]]|xs<-[1..]]

但很明显它不起作用,有什么建议吗?

感谢。

4 个答案:

答案 0 :(得分:4)

我建议尝试递归:要获得n的分区,迭代数字i = 1到n,并递归生成(ni)的分区,基本情况是1的唯一分区本身是1,并且0的分区是空列表。

答案 1 :(得分:3)

这个怎么样......

import Data.List (nub, sort)

parts :: Int -> [[Int]]
parts 0 = []
parts n = nub $ map sort $ [n] : [x:xs | x <- [1..n`div`2], xs <- parts(n - x)]

试一试:

*Main Control.Monad> forM [1..5] (print . parts)
[[1]]
[[2],[1,1]]
[[3],[1,2],[1,1,1]]
[[4],[1,3],[1,1,2],[1,1,1,1],[2,2]]
[[5],[1,4],[1,1,3],[1,1,1,2],[1,1,1,1,1],[1,2,2],[2,3]]

我认为这是正确的,如果效率不高的话。

答案 2 :(得分:2)

我对Haskell有点生疏,但也许以下代码可以指导您找到解决方案。

parts :: Int -> Int -> [[Int]]
parts 0 p = [[]]
parts x p = [(y:ys) | y <-[p..x], ys <- (parts (x - y) y)]

然后你必须调用x = n和p = 1的部分。

修改

当x等于0时,我修复了基本情况,以返回包含单个项目的列表,该项目为空列表。现在它工作正常:))

答案 3 :(得分:2)

我发现定义一个辅助函数partitionsCap很有帮助,它不允许任何项大于给定值。递归使用,它只能用于产生您想要的monotonically递减结果(例如,当您已经[1,3,1]时没有[1,1,3]):

partitions :: Int -> [[Int]]
partitions n = partitionsCap n n

partitionsCap :: Int -> Int -> [[Int]]
partitionsCap cap n
    | n < 0  = error "partitions: negative number"
    | n == 0 = [[]]
    | n > 0  = [i : p | i <- [hi,hi-1..1], p <- partitionsCap i (n-i)]
               where hi = min cap n

算法的核心是,在对N进行分区时,将in降低到1,并将i添加到n-i的分区}。简化为:

concat [map (i:) $ partitions (n-i) | i <- [n,n-1..1]]

但错了:

> partitions 3
[[3],[2,1],[1,2],[1,1,1]]

我们希望[1,2]消失。因此,我们需要限制我们前面提到的分区,以便它们不会超过i

concat [map (i:) $ partitionsCap i (n-i) | i <- [hi,hi-1..1]]
where hi = min cap n

现在,要清理它: concat map 如此接近,引起了我的注意。一点背景:列表推导和列表monad是very closely related concatMap >>=相同,其参数在列表monad中翻转。所以我想知道:那些 concat map 能不能以某种方式变成>>=,并且>>=能以某种方式甜言蜜语地进入列表理解?

在这种情况下,答案是肯定的: - )

[i : p | i <- [hi,hi-1..1], p <- partitionsCap i (n-i)]
where hi = min cap n