在这种情况下,Racket 流是如何工作的?
How Racket streams work in this case?
我目前正在学习 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 创建的元素?它是如何工作的?
感谢您的帮助!
这是一个标准技巧,可以根据其前一个元素定义惰性流。将流视为值的无限序列:
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
。但是当我们去评估流的第三个元素f(x1)
时,我们需要知道x1
。幸运的是,我们刚刚评估了 x1
,因为它是 f(x0)
!这意味着我们可以一次“展开”序列一个元素,其中每个元素都是根据前一个元素定义的:
f(x) = x * 2
s = 1, map(f, s)
= 1, f(x0), f(x1), f(x2), ...
= 1, f(1), f(x1), f(x2), ...
= 1, 2, f(x1), f(x2), ...
= 1, 2, f(2), f(x2), ...
= 1, 2, 4, f(x2), ...
= 1, 2, 4, f(4), ...
= 1, 2, 4, 8, ...
这种打结之所以有效,是因为流是延迟求值的,因此每个值都是按需从左到右计算的。因此,在需要后续元素时,前面的每个元素都已计算完毕,自引用不会造成任何问题。
我目前正在学习 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 创建的元素?它是如何工作的?
感谢您的帮助!
这是一个标准技巧,可以根据其前一个元素定义惰性流。将流视为值的无限序列:
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
。但是当我们去评估流的第三个元素f(x1)
时,我们需要知道x1
。幸运的是,我们刚刚评估了 x1
,因为它是 f(x0)
!这意味着我们可以一次“展开”序列一个元素,其中每个元素都是根据前一个元素定义的:
f(x) = x * 2
s = 1, map(f, s)
= 1, f(x0), f(x1), f(x2), ...
= 1, f(1), f(x1), f(x2), ...
= 1, 2, f(x1), f(x2), ...
= 1, 2, f(2), f(x2), ...
= 1, 2, 4, f(x2), ...
= 1, 2, 4, f(4), ...
= 1, 2, 4, 8, ...
这种打结之所以有效,是因为流是延迟求值的,因此每个值都是按需从左到右计算的。因此,在需要后续元素时,前面的每个元素都已计算完毕,自引用不会造成任何问题。