Stream SICP第3.5.2章无限流整数定义
我正在阅读SICP,很难理解为无限流提供的一个示例: 通过使用诸如add streams之类的操作来操作流,我们可以做更多有趣的事情,这会生成两个给定流的元素和:62 现在我们可以如下定义整数: 显然,我能理解Stream SICP第3.5.2章无限流整数定义,stream,scheme,infinite,sicp,lazy-sequences,Stream,Scheme,Infinite,Sicp,Lazy Sequences,我正在阅读SICP,很难理解为无限流提供的一个示例: 通过使用诸如add streams之类的操作来操作流,我们可以做更多有趣的事情,这会生成两个给定流的元素和:62 现在我们可以如下定义整数: 显然,我能理解整数定义背后的意图,但我正努力在头脑中“模拟”这个流。前面的例子不是问题,因为状态的维护是明确的。例如在本例中: (define (integers-starting-from n) (cons-stream n (integers-starting-from (+ n 1))))
整数定义背后的意图,但我正努力在头脑中“模拟”这个流。前面的例子不是问题,因为状态的维护是明确的。例如在本例中:
(define (integers-starting-from n)
(cons-stream n (integers-starting-from (+ n 1))))
(define integers (integers-starting-from 1))
我理解整数的这个定义没有问题
这本书描述了one
的定义:
(定义一个(cons-stream 1个))
这与递归过程的定义非常相似:1是一对,其car为1,其cdr承诺对1进行求值。评估cdr再次给我们一个1和一个评估cdr的承诺,依此类推
也许这句话让我感到厌烦。其中一个很简单,因为在每个流cdr
上都会评估该过程,并提供一个新的“1”和下一个承诺
当我尝试将此推理应用于整数时
,我很难理解为什么结果流不是“1 2 2…”,因为整数不断地被重新计算,并且基本上是在1处重新开始
编辑
我没有详细说明在我的问题中是否应该假定回忆录是失职的。SICP确实提到了答案中提出的二次行为问题,并以记忆延迟
函数的形式提供了解决方案:
(定义(备忘录过程)
(let((已运行?错误)(结果错误))
(lambda()
(如果(尚未运行?)
(开始(设置!结果(过程))
(设置!已运行?为真)
结果)
结果))
然后定义延迟,使(延迟)等于
(备忘录程序(lambda())
请参阅
cons-stream
必须是一种特殊形式。如果cons-stream
是一个过程,那么根据我们的评估模型,评估(cons-stream)
将自动导致评估
,这正是我们不希望发生的
我这里缺少的一点是,整数
根本没有被重新计算。
addstreams
返回的承诺是每个输入流的stream cdr
。
前面提到的“状态”保持在反馈回路中。
它相当令人费解,老实说,它的力量似乎仍然很神奇 如果我们的流被记忆,那么作为参数传递给add streams
的整数总是“落后于”我们正在枚举的整数,因此它总是可以访问记忆值。(括号)中的数字表示记忆值的使用:
Integers: 1, add-streams / Ones: 1, 1, 1, 1, 1, 1, ...
\Integers: 1, (2), (3), (4), (5), (6), ...
=== === === === === ===
Results: 1 2, 3, 4, 5, 6, 7, ...
Memoized: 2, 3, 4, 5, 6,
因此,100
是通过添加one
99次的元素和整数的流车
生成的,该流车是之前99次调用整数
的结果
虽然第一个添加流
仅合并两个流,但第二个流(返回1
后)将返回来自新添加流
的结果,其中第二个流将是另一个添加流
的结果:
1, add-streams / 1, 1, 1, ...
\ 1, add-streams / 1, 1, ...
\ 1, add-streams / 1, ...
\ 1, add-streams ...
因此,addstreams
,有点像使用cons
创建一个列表,正在创建一对流,其中第一个是一对流,第二个是另一对流
如果不记忆,这不是整数的实际实现,因为它的性能是O(n^2):
访问元素的时间
CPU时间要素
整数(毫秒)
========== ========
第一届0
第二名
第四届0
第8届0
第16届0
32街47号
第64届78
128第313条
第256届1171
第512届4500
第1024页17688
2048第66609条
4096第272531页
通过最简单的非记忆流实现,我们得到:
(define (stream-map2 f s1 s2)
(cons (f (car s1) (car s2))
(lambda ()
(stream-map2 f ((cdr s1)) ((cdr s2))))))
(define ones (cons 1 (lambda () ones)))
(define integers
(cons 1
(lambda ()
(stream-map2 + ones integers))) ;; 1
=
(cons 1
(lambda ()
(cons (+ (car ones) (car integers))
(lambda ()
(stream-map2 + ones
(stream-map2 + ones integers)))))) ;; 2
=
(cons 1
(lambda ()
(cons (+ (car ones) (car integers))
(lambda ()
(let ((i2 (stream-map2 + ones integers)))
(stream-map2 + ones i2))))))
i、 e
=
(缺点1)
(lambda()
(cons(+(car-one)(car-integer))
(lambda()
(让((i2)(cons(+(car-one)(car-integers));;如果你正努力在头脑中模拟它,为什么不用笔和纸(或一些电子等价物)来评估它呢?SICP教给你Scheme使用的评估模型,所以这应该可以机械地进行。如果你已经这样做了,但仍然不理解,你可以指出你没有得到的步骤。用显式状态检查这个替代方案,看看它是否对你更清楚。有时(通常?)同时从两个不同的角度来看问题是有帮助的。我认为这根本不正确。我认为(取n个整数)
是一个O(n)运算;根据这个答案,它必须是O(n^2)。这是可以测试的。在任何情况下,cons stream
的特定实现都在使用中(或等价地,force
)必须根据Scheme的评估规则检查并遵循特定测试代码的评估步骤才能确定。我不再那么确定了。我已经添加了答案,但需要完成…我测试了性能,没有记忆,它是二次的。我还验证了在访问第n个元素add streams时
被调用n次。(正如预期的那样,因为整数调用添加流,但是整数也是添加流的参数之一)对吧。事实上,对于非记忆流来说,看起来像的流和整数都是重新输入的。这样做很累
integers 1
ones 1, 1, 1, 1, 1, 1, ...
integers 1
ones 1, 1, 1, 1, 1, ...
integers 1
ones 1, 1, 1, 1, ...
integers 1
ones 1, 1, 1, ...
integers 1
ones 1, 1, ...
integers 1
ones 1, ...
integers 1
== == == == == == ==
1, 2, 3, 4, 5, 6, 7, ...
1, add-streams / 1, 1, 1, ...
\ 1, add-streams / 1, 1, ...
\ 1, add-streams / 1, ...
\ 1, add-streams ...
Time to Access Elements
Element of CPU Time
integers (msec)
========== ========
1 st 0
2 nd 0
4 th 0
8 th 0
16 th 0
32 nd 47
64 th 78
128 th 313
256 th 1,171
512 th 4,500
1,024 th 17,688
2,048 th 66,609
4,096 th 272,531
(define (stream-map2 f s1 s2)
(cons (f (car s1) (car s2))
(lambda ()
(stream-map2 f ((cdr s1)) ((cdr s2))))))
(define ones (cons 1 (lambda () ones)))
(define integers
(cons 1
(lambda ()
(stream-map2 + ones integers))) ;; 1
=
(cons 1
(lambda ()
(cons (+ (car ones) (car integers))
(lambda ()
(stream-map2 + ones
(stream-map2 + ones integers)))))) ;; 2
=
(cons 1
(lambda ()
(cons (+ (car ones) (car integers))
(lambda ()
(let ((i2 (stream-map2 + ones integers)))
(stream-map2 + ones i2))))))
=
(cons 1
(lambda ()
(cons (+ (car ones) (car integers))
(lambda ()
(let ((i2 (cons (+ (car ones) (car integers)) ;; <---- 1
(lambda ()
(stream-map2 + ones
(stream-map2 + ones integers))))))
(cons (+ (car ones) (car i2))
(lambda ()
(stream-map2 + ones ((cdr i2))))))))))
=
(cons 1
(lambda ()
(cons (+ (car ones) (car integers))
(lambda ()
(cons (+ (car ones)
(+ (car ones) (car integers)))
(lambda ()
(stream-map2 + ones
(stream-map2 + ones
(stream-map2 + ones integers))))))))) ;; 3
=
....