哈斯克尔:把一个非无限的列表变成无限的一个+懒惰(euler 391)

时间:2012-09-04 00:13:16

标签: haskell

我编写了下面的Haskell代码来生成一个列表,其中第n个元素是写入1..n作为二进制数的1的数量(它与euler 391相关,顺便提一下):

buildList :: a -> (a -> a) -> [a]
buildList start f = start : buildList (f start) f

differences :: [[Int]]
differences = buildList [0] (\x -> x ++ map (+1) x)

sequenceK' :: Int -> [Int]
sequenceK' n = tail $ scanl (+) 0 (last $ take n differences)

导致sequenceK' n给出2 ^(n-1)个元素的列表。

这个问题分为两部分:

a)为什么计算head $ sequenceK' n所花费的时间会随着n的增加而增加? - 由于ghc的懒惰,我希望时间或多或少保持不变。

b)是否可以定义此列表的无限版本,以便我可以执行taketakeWhile之类的操作,而无需担心传递给{{1}的参数的值}?

1 个答案:

答案 0 :(得分:5)

a)因为您正在调用last $ take n differences,而n需要做更多工作。

b)是的,这是可能的。思路最少的解决方案就是采用我们在每个特定深度看到的最早元素:

*Main> take 20 . map head . transpose $ differences
[0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,1,2,2,3]

更好的解决方案是仅生成有意义的位。我们可以通过观察以下相等来做到这一点:

differences' = 1 : (differences' >>= \x -> [x, x+1])

实际上,这有点偏,你可能会猜到:

*Main> take 20 differences'
[1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,1,2,2,3,2,3]

但只需在前面添加0即可轻松修复。