方案:使用`delay`表达式时有关显示的问题

问题描述 投票:2回答:1

这是与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为什么打印所有数字?

stream scheme delay sicp
1个回答
2
投票

问题出在这个定义上:

(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

© www.soinside.com 2019 - 2024. All rights reserved.