【问题标题】:Scheme: problem about display when using `delay` expression方案:使用 `delay` 表达式时的显示问题
【发布时间】:2020-03-01 03:58:45
【问题描述】:

这是一个与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】:

    问题在于这个定义:

    (define (cons-stream x y)
      (cons x (delay y)))
    

    它将cons-stream定义为一个函数,因为它使用define

    Scheme 的评估是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) 以强制流的尾部。

    您可以在this answer 的末尾看到这样的基于lambda 的流代码,或者在没有任何宏的情况下使用显式lambdas 代替ideone entry(对于this RosettaCode entry)。不过,这种方法可以改变代码的性能,因为delay 正在记忆,但基于lambda 的流不是。如果我们尝试多次访问流的元素,就会发现差异。

    另请参阅this answer 以了解有关流实现的另一种看法,通过手术将列表的最后一个 cons 单元格修改为记忆 force

    【讨论】:

    • 只是将其放在本书的上下文中,“急切”的评估就是本书所说的应用顺序。您在原始定义中遇到的问题在第 3.5.1 节的脚注 56 中进行了介绍-此时我们没有实现自己的 cons-stream 的工具,但这将在第 4.1 节中介绍,在那里我们学到了很多东西更多关于特殊形式和 4.2 我们实施必要的正常顺序(惰性)评估的地方。
    • 谢谢,但是 (cons..delay) 本质上与 (cons-stream...) 相同,对吧?如果 y 在传递给延迟时已经计算,为什么如果将 lambda (x) (+ x 1) 传递给流映射,输出是 (1 . #) 而不是 1234567891011?
    • 不,如果我们这样手动写出来,就没有y。没有函数调用,只有嵌套表达式,delay 中的表达式确实会延迟。所以如果我们这样做,在我们的代码中不会有任何对cons-stream的调用,只会调用consdelay,比如(cons A (delay B))。而delay 一个宏(或特殊形式,无论如何),它在工作之前评估它的参数,而是直接操作参数表达式。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-12
    • 2020-06-08
    • 1970-01-01
    • 2014-07-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多