我正在学习 Haskell,我在Haskell Wiki上的以下表达 真的让我感到困惑:
fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
我不太明白为什么会这样。
如果我应用标准 Currying 逻辑(zipWith (+))
,则返回一个将列表作为参数的函数,然后返回另一个将另一个列表作为参数的函数,并返回一个列表 ( zipWith::(a -> b -> c) -> [a] -> [b] -> [c]
)。因此,fibs
是对列表(尚未评估)的引用,并且(tail fibs)
是相同(未评估)列表的尾部。当我们尝试计算 ( take 10 fibs
) 时,前两个元素绑定到0
和1
。换句话说fibs==[0,1,?,?...]
,和(tail fibs)==[1,?,?,?]
。第一次加法完成fibs
后就变成了[0,1,0+1,?,..]
。同样,在第二次加法之后,我们得到[0,1,0+1,1+(0+1),?,?..]
- 我的逻辑正确吗?
- 有没有更简单的方法来解释这一点?(知道 Haskell 编译器对此代码做了什么的人的任何见解?)(欢迎链接和参考)
- 确实这种类型的代码只是因为惰性求值才有效吗?
- 当我这样做时会发生什么评估
fibs !! 4
? - 这段代码是否假设 zipWith 从前到后处理元素?(我认为不应该,但我不明白为什么不)
EDIT2:我刚刚发现上面的问题在这里得到了很好的回答。如果我浪费了任何人的时间,我很抱歉。
编辑:这是一个更难理解的案例(来源:Project Euler 论坛):
filterAbort :: (a -> Bool) -> [a] -> [a]
filterAbort p (x:xs) = if p x then x : filterAbort p xs else []
main :: Int
main = primelist !! 10000
where primelist = 2 : 3 : 5 : [ x | x <- [7..], odd x, all (\y -> x `mod` y /= 0) (filterAbort (<= (ceiling (sqrt (fromIntegral x)))) primelist) ]
请注意,all (\y -> x mod y /= 0)...
这里如何引用 x 不会导致无限递归?