如何在方程式右侧的下方排队,可以使用符号'fibs',尽管尚未确定:
let fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
答案 0 :(得分:19)
重点是fibs
fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
在其他地方使用之前不会被评估。然后使用已知部分展开定义。我们从fibs = 0 : 1 : ???
开始。然后,如果需要第三个元素,则进一步评估定义,
fibs = 0 : 1 : zipWith (+) (0 : 1 : ???) (tail (0 : 1 : ???))
= 0 : 1 : zipWith (+) (0 : 1 : ???) (1 : ???)
= 0 : 1 : (0 + 1) : zipWith (+) (1 : ???) (???)
但是未知部分???
已经部分已知,已被确定为??? = 1 : ????
,因此展开可以继续,
= 0 : 1 : 1 : zipWith (+) (1 : 1 : ????) (1 : ????)
= 0 : 1 : 1 : 2 : zipWith (+) (1 : ????) (????)
-- now ???? is known to be 2:?????
= 0 : 1 : 1 : 2 : zipWith (+) (1 : 2 : ?????) (2 : ?????)
等
答案 1 :(得分:6)
实际上,在您的定义中调用fibs
之前,您的程序中稍后会使用fibs
,此时fibs
已完全定义< / em>的
您也可以在大多数其他语言中执行此操作:
int foo(int x)
{
if (x <= 0) return 0;
// will call foo when it gets there, at which point its been defined
foo(x - 1);
}
答案 2 :(得分:4)
所有 Haskell绑定都是递归的。这与大多数语言不同,但由于懒惰,它通常可以正常工作(与大多数流行语言相比,Haskell评估是非严格的)。当他们尝试以下事情时,新手经常被绊倒:
main = do
let a = 3
let a = 3 + a
print a
因为第二次绑定到a
实际上忽略并隐藏第一个,并根据自身定义a
,这会在您尝试打印时导致无限循环3 + 3 + 3 + 3 + ...
无限列表的一个更简单的例子是ones
:1
的无限列表
ones = 1 : ones
在这种情况下,只是简单地引用自己
_______
| |
v |
________ |
| ones | |
| 1 : ---|
--------
在Haskell中,您可以创建无限树,就像创建无限列表一样:
data Tree a = Stub | Branch a (Tree a) (Tree a)
onesTree = Branch 1 onesTree onesTree
______ _______
| | | |
| v v |
| ____________ |
| | onesTree | |
|--- | 1 | ----|
------------
我认为真正的问题是:为什么其他语言不像Haskell那样方便地支持递归值?
答案 3 :(得分:1)
嗯,要了解这一点,了解如何实施懒惰评估是很好的。基本上,未评估的表达式由 thunks 表示:一种数据结构,表示在实际需要时计算值所需的所有信息。当后者发生时(或者正如我们所说,当thunk 强制时),执行计算值的代码,并将thunk的内容替换为结果 - 其中可能有指向其他thunk的指针
所以fibs
开始是一个thunk。这个thunk包含指向用于计算其值的代码的指针,以及指向此代码作为参数的thunk的指针。后面这些指针之一是指向fibs
本身的指针。