在定义之前使用符号

时间:2012-03-25 21:14:34

标签: haskell

如何在方程式右侧的下方排队,可以使用符号'fibs',尽管尚未确定:

let fibs = 0 : 1 : zipWith (+) fibs (tail fibs)

4 个答案:

答案 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 + ...

的结果

无限列表的一个更简单的例子是ones1的无限列表

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本身的指针。