这是与SICP中的ex3.51有关的问题,这是代码
(define (cons-stream x y)
(cons x (delay y)))
(define (stream-car stream) (car stream))
(define (stream-cdr stream) (force (cdr stream)))
(define (stream-map proc s)
(if (stream-null? s)
the-empty-stream
(cons-stream
(proc (stream-car s))
(stream-map proc (stream-cdr s)))))
(define (stream-enumerate-interval low high)
(if (> low high)
the-empty-stream
(cons-stream
low
(stream-enumerate-interval (+ low 1) high))))
(define (stream-ref s n)
(if (= n 0)
(stream-car s)
(stream-ref (stream-cdr s) (- n 1))))
(define (show x)
(display x)
x)
;test
(stream-map show (stream-enumerate-interval 0 10))
输出为012345678910(0 . #<promise>)
。
但是我认为cons-stream
中的延迟表达式延迟了评估,如果我在stream-map
中使用了与lambda (x) (+ x 1)
类似的不同处理函数,则输出(1 . #<promise>)
更合理,那么display
为什么打印所有数字?
问题出在这个定义上:
(define (cons-stream x y)
(cons x (delay y)))
由于使用cons-stream
,因此将define
定义为函数。
方案的评估是eager:在输入函数体之前评估参数。因此,当y
传递到delay
时已经被完全计算出来。
相反,cons-stream
应该定义为宏,例如
(define-syntax cons-stream
(syntax-rules ()
((_ a b) (cons a (delay b)))))
或我们可以手动手动显式调用delay
,例如
(define (stream-map proc s)
(if (stream-null? s)
the-empty-stream
(cons
(proc (stream-car s))
(delay
(stream-map proc (stream-cdr s))))))
然后我们的代码中将不会调用cons-stream
,只有(cons A (delay B))
调用。delay
是
宏(或特殊形式,无论如何),它在工作之前不评估其参数,而是直接处理参数表达式。而且我们甚至可以将呼叫转移到delay
,然后将(cons A (delay B))
替换为(cons A (lambda () B))
。这也将重新实现force
(内置的,并与内置delay
一起使用),只需简单地(define (force x) (x))
或在适当的情况下手动调用(x)
来强制流尾即可。
[您可以在lambda
的末尾看到此类基于this answer的流代码,或者使用显式lambda而不使用任何宏的ideone entry(对于this RosettaCode entry)。但是,这种方法可以更改代码的性能,因为delay
正在存储,而基于lambda
的流则没有。如果我们尝试不止一次访问流元素,就会看到差异。
[另请参阅this answer以获取流实现的另一种方法,以手术方式修改列表的最后一个cons单元格作为提示force
。