Haskell 有人能解释这个懒惰的斐波那契解吗?

Haskell 有人能解释这个懒惰的斐波那契解吗?,haskell,stream,lazy-evaluation,fibonacci,lazy-sequences,Haskell,Stream,Lazy Evaluation,Fibonacci,Lazy Sequences,代码如下: fibs = 0 : 1 : zipWith (+) fibs (drop 1 fibs) 计算时,fibs是Fibonacci数的无限列表。我不明白的是列表是如何连接的 zipWith返回一个列表,因此压缩fibs将产生以下结果: 0 : 1 : [1] : [1,2] : [1,2,3] 因为0:1:zipWith(+)[0,1][1]产生[1],zipWith(+)[0,1,1][1,1]产生[1,2]等) 然而,当我运行代码时,我得到了正确的结果 我在这里不明白什么?你的

代码如下:

fibs = 0 : 1 : zipWith (+) fibs (drop 1 fibs)
计算时,
fibs
是Fibonacci数的无限列表。我不明白的是列表是如何连接的

zipWith
返回一个列表,因此压缩
fibs
将产生以下结果:

0 : 1 : [1] : [1,2] : [1,2,3]

因为
0:1:zipWith(+)[0,1][1]
产生
[1]
zipWith(+)[0,1,1][1,1]
产生
[1,2]
等)

然而,当我运行代码时,我得到了正确的结果

我在这里不明白什么?

你的“因为”并不是全部。你正在截断“到目前为止的故事”的列表,并热切地进行评估,然后想知道其余的内容从何而来。这还不足以理解到底发生了什么,所以问得好

定义时计算的是什么

fibs = 0 : 1 : zipWith (+) fibs (drop 1 fibs)
??很少。一旦开始使用列表,计算就开始了。惰性计算只在需要时发生

什么是需求?你可以问“你是
[]
还是
x:xs
?”,如果是后者,你就可以处理这些碎片

当我们问fibs的问题时,我们得到了答案

fibs = x0 : xs0
x0  = 0
xs0 = 1 : zipWith (+) fibs (drop 1 fibs)
xs0 = x1 : xs1
x1  = 1
xs1 = zipWith (+) (0 : xs0) (drop 1 (0 : xs0))
但这意味着(代替
fibs
x0

当我们再次询问时,我们得到了答案

fibs = x0 : xs0
x0  = 0
xs0 = 1 : zipWith (+) fibs (drop 1 fibs)
xs0 = x1 : xs1
x1  = 1
xs1 = zipWith (+) (0 : xs0) (drop 1 (0 : xs0))
所以

但现在它变得有趣了,因为我们必须做一些工作。只要足够的工作来回答这个问题,介意吗?当我们查看
xs1
时,我们强制
zipWith
,这会强制
放下

xs1 = zipWith (+) (0 : 1 : xs1) (drop 1 (0 : 1 : xs1))
    = zipWith (+) (0 : 1 : xs1) (1 : xs1)
    = (0 + 1) : zipWith (+) (1 : xs1) xs1
所以

看到了吗?我们坚持认为,我们仍然知道一个压缩列表的前两个元素,以及另一个压缩列表的第一个元素。这意味着我们将能够交付下一个输出并刷新“缓冲区”。当我们看
xs2
时,我们得到

xs2 = zipWith (+) (1 : 1 : xs2) (1 : xs2)
    = (1 + 1) : zipWith (1 : xs2) xs2
xs2 = x3 : xs3
x3  = 1 + 1 = 2
xs3 = zipWith (1 : xs2) xs2
    = zipWith (1 : 2 : xs3) (2 : xs3)
我们很高兴再次出发

每次我们需要下一个元素时,我们也会离
zipWith
元素用尽更进一步,这也正是关键时刻

使值在关键时刻出现的规则中没有一个是在类型中表示的。目前,程序员需要确保类型良好的程序不会在发出请求时因数据耗尽而出错。(我计划对此做些什么,但我会尽量不离题。)


关键是,懒惰的“按需”计算意味着我们不必将列表截断为流程启动时可以看到的元素。我们只需要知道,我们总是可以采取下一步。

为什么投票被否决?你从哪里得到了
0:1:[1,2]:[1,2,3]
?因为
0:1:zipWith(+)[0,1][1]
产生了
[1]
zipWith(+)[0,1,1][1,1]
产生了
[1,2]
等。它不是压缩
[0,1,1,1]…]
[1,1,…]
。它也没有列出中间列表。那它在做什么呢?你今天真的很快。我正在大量地替换我必须做但不想做的事情。在题外话中表达的抱怨是相当恼人的,因为它在
数据.Sequence
中实现了
zipWith
,以及与
Traversable
,它依赖于相同结构的两次遍历,两次遍历的元素数相同。
xs2 = zipWith (+) (1 : 1 : xs2) (1 : xs2)
    = (1 + 1) : zipWith (1 : xs2) xs2
xs2 = x3 : xs3
x3  = 1 + 1 = 2
xs3 = zipWith (1 : xs2) xs2
    = zipWith (1 : 2 : xs3) (2 : xs3)