Haskell 函数中的自引用
我正在学习 Haskell,并且在 Haskell Wiki 上使用以下表达式 我真的很困惑:
fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
我不太明白为什么会这样。
如果我应用标准柯里化逻辑 (zipWith (+))
返回一个函数,将列表作为参数,然后返回另一个函数,该函数将另一个列表作为参数,并返回一个列表 (zipWith::(a -> b -> c) -> [a] -> [b] -> [c])。因此,
fibs
是对列表(尚未评估)的引用,(tail fibs)
是同一(未评估)列表的尾部。当我们尝试评估(取 10 个 fib
)时,前两个元素绑定到 0
和 1
。换句话说,fibs==[0,1,?,?...]
和 (tail fibs)==[1,?,?,?]
。第一次加法完成后,fibs
变为 [0,1,0+1,?,..]
。同样,在第二次加法之后,我们得到 [0,1,0+1,1+(0+1),?,?..]
- 我的逻辑正确吗?
- 有没有更简单的方法来解释这一点? (知道 Haskell 编译器对这段代码做了什么的人有什么见解吗?)(欢迎链接和参考)
- 这种类型的代码确实只因为惰性求值才有效吗?
- 当我撒谎时会发生什么评估! 4?
- 此代码是否假设 zipWith 从头到尾处理元素? (我认为不应该,但我不明白为什么不)
编辑2:我刚刚发现上面的问题被问到并且得到了很好的回答 此处。如果我浪费了任何人的时间,我很抱歉。
编辑:这是一个更难理解的情况(来源:项目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 不会导致无限递归?
I am learning Haskell and I the following expression on Haskell Wiki
really puzzled me:
fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
I can't quite figure out why this works.
If I apply standard Currying logic (zipWith (+))
returns a function takes list as an argument and, in turn, returns another function that takes another list as an argument, and returns a list (zipWith::(a -> b -> c) -> [a] -> [b] -> [c]
). So, fibs
is a reference to a list (that has not yet been evaluated) and (tail fibs)
is the tail of the same (unevaluated) list. When we try to evaluate (take 10 fibs
), the first two elements are bound to 0
and 1
. In other words fibs==[0,1,?,?...]
and (tail fibs)==[1,?,?,?]
. After the first addition completes fibs
becomes [0,1,0+1,?,..]
. Similarly, after the second addition we get [0,1,0+1,1+(0+1),?,?..]
- Is my logic correct?
- Is there a simpler way to explain this? (any insights from people who know what Haskell complier does with this code?) (links and reference are welcome)
- It is true that this type of code only works because of lazy evaluation?
- What evaluations happen when I do
fibs !! 4
? - Does this code assume that zipWith processes elements first to last? (I think it should not, but I don't understand why not)
EDIT2: I just found the above question asked and well answered here. I am sorry if I wasted anyone's time.
EDIT: here is a more difficult case to understand (source: Project Euler forums):
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) ]
Note that all (\y -> x mod y /= 0)...
How can referring to x here NOT cause infinite recursion?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我将为您跟踪评估:
With:
So:
等等。因此,如果您索引此结构,它将强制评估 fibs 的每一步,直到找到索引。
为什么这样有效?一个词:分享。
当计算
fibs
时,它会在堆中增长,记录已计算的值。稍后对fibs
的反向引用可以重用之前为fibs
计算的所有值。免费记忆!该共享在堆中是什么样子的?
当您询问列表末尾的对象时,Haskell 会计算下一个值并记录它,并将该自引用向下移动一个节点。
这个终止的事实主要取决于懒惰。
I'll trace the evaluation for you:
With:
So:
Etc. So if you index into this structure, it will force each step of fibs to be evaluated until the index is found.
Why is this efficient? One word: sharing.
As
fibs
is computed, it grows in the heap, recording values that have been computer. Later back references tofibs
get to reuse all previously computed values forfibs
. Free memoization!What does that sharing look like in the heap?
As you ask for the object on the end of the list, Haskell computes the next value, records it, and moves that self-reference down a node.
The fact this terminates relies crucially on laziness.