在这种情况下,Racket流如何工作?

时间:2018-01-07 17:59:00

标签: stream scheme racket self-reference sicp

我目前正在学习Racket(只是为了好玩),我偶然发现了这个例子:

(define doubles
   (stream-cons
     1
     (stream-map 
        (lambda (x) 
            (begin
                (display "map applied to: ")
                (display x)
                (newline)
                (* x 2)))
        doubles)))

它产生 1 2 4 8 16 ...

我不太明白它是如何运作的。

所以它创建 1 作为第一个元素;当我调用(stream-ref doubles 1)时,它会创建第二个元素,显然是 2

然后我调用(stream-ref doubles 2)这应强制创建第三个元素,以便为已有2个元素的流调用stream-map - (1 2) - 因此它应该生成(2 4)然后将此结果追加到流中。

为什么此stream-map始终应用于 last 创建的元素?它是如何工作的?

感谢您的帮助!

1 个答案:

答案 0 :(得分:0)

这是一个标准技巧,可以根据前一个元素定义延迟流。将流视为无限的值序列:

s = x0, x1, x2, ...

现在,当您在流上map时,您提供了一个函数并生成一个新流,该函数应用于流的每个元素:

map(f, s) = f(x0), f(x1), f(x2), ...

但是,根据自身的映射定义流时会发生什么?好吧,如果我们有一个流s = 1, map(f, s),我们可以扩展该定义:

s = 1, map(f, s)
  = 1, f(x0), f(x1), f(x2), ...

现在,当我们真正去评估流的第二个元素f(x0)时,x0显然是1,因为我们将流的第一个元素定义为{ {1}}。但是当我们去评估流的第三个元素1时,我们需要知道f(x1)。幸运的是,我们刚刚评估了x1,因为它是x1!这意味着我们可以一次“展开”一个元素的序列,其中每个元素都是根据前一个元素定义的:

f(x0)

这种打结工作是因为流被懒惰地评估,所以每个值都是按需从左到右计算的。因此,每个前一个元素都是在需要后续元素时计算出来的,并且自引用不会导致任何问题。