我编写了下面的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)是否可以定义此列表的无限版本,以便我可以执行take
和takeWhile
之类的操作,而无需担心传递给{{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
即可轻松修复。