Haskell函数中的自引用

Vla*_*sky 8 recursion haskell lazy-evaluation self-reference

我正在学习Haskell,而Haskell Wiki上的以下表达式 让我很困惑:

fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
Run Code Online (Sandbox Code Playgroud)

我无法弄清楚为什么会这样.

如果我应用标准Currying逻辑,则(zipWith (+))返回一个函数将list作为参数,然后返回另一个函数,该函数将另一个列表作为参数,并返回一个list(zipWith::(a -> b -> c) -> [a] -> [b] -> [c]).因此,fibs是对列表的引用(尚未评估),并且(tail fibs)是相同(未评估)列表的尾部.当我们尝试evaluate(take 10 fibs)时,前两个元素绑定到01.换句话说fibs==[0,1,?,?...](tail fibs)==[1,?,?,?].第一次添加完成后fibs变为[0,1,0+1,?,..].同样,在第二次添加之后我们得到了[0,1,0+1,1+(0+1),?,?..]

  • 我的逻辑是否正确?
  • 有没有更简单的方法来解释这个?(知道Haskell编译器对此代码做什么的人的任何见解?)(欢迎链接和参考)
  • 确实,这种类型的代码只能用于懒惰的评估吗?
  • 我做什么评价fibs !! 4
  • 此代码是否假设zipWith首先处理元素?(我认为不应该,但我不明白为什么不)

编辑2:我刚刚发现上述问题并在此处得到很好的解答.如果我浪费了任何人的时间,我很抱歉.

编辑:这是一个更难理解的案例(来源: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) ]
Run Code Online (Sandbox Code Playgroud)

请注意,all (\y -> x mod y /= 0)... 这里如何引用x不会导致无限递归?

Don*_*art 15

我将为您追踪评估:

> fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
Run Code Online (Sandbox Code Playgroud)

附:

> zipWith f (a:as) (b:bs) = f a b : zipWith f as bs
> zipWith _ _      _      = []

> tail (_:xs)             =  xs
> tail []                 =  error "tail" 
Run Code Online (Sandbox Code Playgroud)

所以:

  fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
? fibs = 0 : 1 : ((+) 0 1 : zipWith (+) (tail fibs) (tail (tail fibs)))
? fibs = 0 : 1 : 1 : ((+) 1 1 : zipWith (+) (tail (tail fibs)) (taii (tail (tail fibs)))))    
? fibs = 0 : 1 : 1 : 2 : ((+) 1 2 : zipWith (+) (tail (tail (tail fibs))) (tail (taii (tail (tail fibs))))))
? fibs = 0 : 1 : 1 : 2 : 3 : ((+) 2 3 : zipWith (+) (tail (tail (tail (tail fibs)))) (tail (tail (taii (tail (tail fibs)))))))
Run Code Online (Sandbox Code Playgroud)

等等.因此,如果您索引此结构,它将强制评估每个fibs步骤,直到找到索引.

为什么效率这么高?一句话:分享.

fibs计算,它会在堆中增长,记录已经是计算机的值.稍后返回引用以fibs重用所有先前计算的值fibs.免费记忆!

堆中的共享是什么样的?

在此输入图像描述

当您在列表末尾请求对象时,Haskell计算下一个值,记录它,并将该自引用向下移动到节点.

这种终止的事实在很大程度上依赖于懒惰.

  • 简化因为我不是专家:Haskell中的每个非严格值都存储在一个名为thunk的结构中.它包含一个值或某种指向可以计算值的代码的指针.因此当你有'0:1:zipWith`时,第3个元素是指向代码的thunk,而不是计算列表的其余部分.当运行该代码时,thunk将被一个cons单元替换,该cons单元具有列表的第3个元素的值,而thunk可以从第4个位置计算列表的其余部分.等等. (3认同)