简体   繁体   English

Haskell无限递归

[英]Haskell infinite recursion

The following function computes the Fibonacci sequence: 以下函数计算Fibonacci序列:

fib = 0 : 1 : (zipWith (+) fib (tail fib))

If we run it, we will get an infinite list, but how does the recursion work? 如果我们运行它,我们将得到一个无限列表,但递归如何工作? Why does it get to print numbers on the screen if it the function keeps calling itself? 如果该功能一直在调用,为什么它会在屏幕上打印数字呢? I would appreciate if you could explain how the compiler manages the calls. 如果您能解释编译器如何管理调用,我将不胜感激。

I've drawn a picture, which you might find helpful. 我画了一张你可能觉得有帮助的照片。
Note that zipWtih op (x:xs) (y:xs) = (op xy):zipWith xs ys , which is how zipWtih appears to "move" right along the list. 请注意, zipWtih op (x:xs) (y:xs) = (op xy):zipWith xs ys ,这就是zipWtih在列表中“向右移动”的方式。 It's reading elements and spitting out sums: 它是读取元素并吐出总和:

pic用彩色指针


Here's a more detailed step-by-step evaluation. 这是一个更详细的逐步评估。 (Although I'll paste copies of what's there, there's only one copy in memory.) I'll use .... for things I can't be bothered to write out. (虽然我会粘贴那些内容的副本,但内存中只有一个副本。)我会使用....因为我不能写出来的东西。

fib = 0:1:zipWith (+) fib (tail fib)
    = 0:1:zipWith (+) (0:1: .... ) (tail (0:1: .... )
    = 0:1:(0+1:zipWith (+) (1:(0+1: .... )) ( 0+1:..... ))
    = 0:1:1:zipWith (+) (1: ....) (......)

notice that now we know that zipWith (+) fib (tail fib) = 1:..... . 注意,现在我们知道zipWith (+) fib (tail fib) = 1:.....

    = 0:1:1:zipWith (+) (1:1: ....) (1:......)
    = 0:1:1:(1+1):zipWith (+) (1:(1+1): .....) ((1+1):....)
    = 0:1:1:2:zipWith (+) (1:2: .....) (2:....)

I'll go a little faster: 我会快一点:

    = 0:1:1:2:(1+2):zipWith (+) (2: .....) (....)
    = 0:1:1:2:3     :zipWith (+) (2:3 .....) (3:....)
    = 0:1:1:2:3:(2+3):zipWith (+) (3:(2+3):.....) ((2+3):.....)
    = 0:1:1:2:3:5     :zipWith (+) (3:5:.....) (5:.....)
    = 0:1:1:2:3:5:8    :zipWith (+) (5:8:....) (8:......)
    = 0:1:1:2:3:5:8:13  :zipWith (+) (8:13:....) (13:......)
    = 0:1:1:2:3:5:8:13:21:zipWith (+) (13:21....) (21:......)

At each stage, the last two arguments to the zipWith function are like pointers to (one and two positions) further up the fib list than we are at present. 在每个阶段, zipWith函数的最后两个参数就像指向(一个和两个位置)的指针,它们在fib列表中比我们目前更进一步。

In a word: laziness. 总之一句话:懒惰。 A list in Haskell is more like a generator: it will only compute values when they are demanded by something else. Haskell中的列表更像是一个生成器:它只会在其他东西需要时计算值。

For instance head [1 , 2+3] will not perform the addition, since it is not needed. 例如, head [1 , 2+3]将不执行添加,因为不需要。 Similarly, if we recursively let ones = 1 : ones , then head ones = head (1 : ones) = 1 does not need to evaluate all the tail. 类似地,如果我们递归地让ones = 1 : ones ,那么head ones = head (1 : ones) = 1不需要评估所有尾部。

You can try guessing what happens if we print a pair x , defined as follows: 您可以尝试猜测如果我们打印一对x会发生什么,定义如下:

x = (n, fst x + 1)

Above we use a (lazy) pair instead of a (lazy) list, but the reasoning is the same. 上面我们使用(懒)对而不是(懒)列表,但推理是相同的。 Don't evaluate anything unless is it needed by something else. 除非其他东西需要,否则不要评估任何东西。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM