SICP 练习 3.52:使用 Scheme (Guile) 是否需要 memo-proc?

SICP Exercise 3.52: Is memo-proc necessary using Scheme (Guile)?

我正在做 SICP ex 3.52。我得到了对 "stream-ref" 和 "display-stream" 表达式的正确响应,并且在练习中给出的每个表达式之后也得到了 "sum" 的正确值。但是,在列出的练习之前,无需使用第 439 页上提供的 "memo-proc" 优化,这一切都有效。

我在 Linux 上使用 Guile,我对代码所做的唯一其他添加是 "cons-stream" 代码顶部的语法定义,以包含 "delay" 形式.

也许我误解了什么,但我什至没有看到在执行中调用 "memo-proc" 的地方。 Guile 是否内置了一些东西,它已经完成了 "memo-proc" 提供的这种优化,根据 SICP,它用于将 "delay" 实现为一个特殊用途的记忆过程。

这是 SICP 中的记忆过程...

(define (memo-proc proc)
  (let ((already-run? false) (result false))
    (lambda ()
      (if (not already-run?)
          (begin (set! result (proc))
                 (set! already-run? true)
                 result)
          result))))

相关代码方便...

(define (stream-ref s n)
  (if (= n 0)
      (stream-car s)
      (stream-ref (stream-cdr s) (- n 1))))

(define (stream-map proc . argstreams)
  (if (stream-null? (car argstreams))
      the-empty-stream
      (cons-stream
        (apply proc (map stream-car argstreams))
        (apply stream-map 
               (cons proc (map stream-cdr argstreams))))))

(define (stream-for-each proc s)
  (if (stream-null? s)
      'done
      (begin (proc (stream-car s))
             (stream-for-each proc (stream-cdr s)))))

(define (display-stream s)
  (stream-for-each display-line s))

(define (display-line x) (newline) (display x))

(define (stream-car stream) (car stream))
(define (stream-cdr stream) (force (cdr stream)))

(define (stream-enumerate-interval low high)
  (if (> low high)
      the-empty-stream
      (cons-stream
        low
        (stream-enumerate-interval (+ low 1) high))))

(define (stream-filter pred stream)
  (cond ((stream-null? stream) the-empty-stream)
        ((pred (stream-car stream))
         (cons-stream (stream-car stream)
                      (stream-filter
                       pred
                       (stream-cdr stream))))
        (else (stream-filter pred (stream-cdr stream)))))

以及练习的表达式序列...

(define sum 0)

(define (accum x) (set! sum (+ x sum)) sum)

(define seq
  (stream-map accum
              (stream-enumerate-interval 1 20)))

(define y (stream-filter even? seq))

(define z
  (stream-filter (lambda (x) (= (remainder x 5) 0))
                 seq))

(stream-ref y 7)

(display-stream z)

我期待下面列出的结果,我确实得到了。

(define sum 0) 
 ;; sum => 0 

 (define (accum x) 
   (set! sum (+ x sum)) 
   sum) 
 ;; sum => 0 

 (define seq (stream-map accum (stream-enumerate-interval 1 20))) 
 ;; sum => 1 

 (define y (stream-filter even? seq)) 
 ;; sum => 6 

 (define z (stream-filter (lambda (x) (= (remainder x 5) 0)) 
                          seq)) 
 ;; sum => 10 

 (stream-ref y 7) 
 ;; sum => 136 
 ;; => 136 

 (display-stream z) 
 ;; sum => 210 
 ;; => (10 15 45 55 105 120 190 210) 

但是,我没有使用 "memo-proc" 就得到了这些结果。但是,如果没有 "memo-proc" 优化,我希望在以下情况下获得 "sum" 15:

(define z (stream-filter (lambda (x) (= (remainder x 5) 0)) 
                          seq)) 
;; sum => 10 

因为 "accum" 函数将作为流的一部分被额外调用 "seq" 需要第二次枚举。我希望有人能帮我看看我错过了什么。

我正在使用 Racket(使用 SICP 包)进行练习,流的默认实现已被记忆(参见:Do Racket streams memoize their elements?)。我想 Guile 也在做同样的事情。

这可能就是问题说“考虑”的原因,因为没有简单的方法来验证非记忆化行为。

在第 4 章中,我们自己实现了语言,对于 ex 4.18,您可以从头开始实现流。使用这些简单的流,我得到了这个练习的以下结果:

Naive Streams
=============
    sum after stream-ref: 162
    sum after display-stream: 362

并且在添加本节的 memo-proc 实现之后:

Naive Streams with Memo-Proc
============================
    sum after stream-ref: 136
    sum after display-stream: 210


更新: 'sum' 在执行期间不同时间的值不仅受记忆影响。它还会受到您使用的是 SICP 样式 'odd' 流还是更传统的 'even' 流的影响,如果使用现代方案,还会受到您是使用内置映射和过滤器还是文本中的映射和过滤器的影响。

+----------------+-------------+-------------+--------------+--------------+
|                | SICP Scheme | SICP Scheme | Racket With  | Racket With  |
|                |    With     |   Without   |    Text's    |   Built in   |
| sum after:     | Memoization | Memoization | Map & Filter | Map & Filter |
+----------------+-------------+-------------+--------------+--------------+
| define accum   |        0    |       0     |        0     |        0     |
+----------------+-------------+-------------+--------------+--------------+
| define seq     |        1    |       1     |        0     |        0     |
+----------------+-------------+-------------+--------------+--------------+
| define y       |        6    |       6     |        6     |        0     |
+----------------+-------------+-------------+--------------+--------------+
| define z       |       10    |      15     |       10     |        0     |
+----------------+-------------+-------------+--------------+--------------+
| stream-ref     |      136    |     162     |      136     |      136     |
+----------------+-------------+-------------+--------------+--------------+
| display-stream |      210    |     362     |      210     |      210     |
+----------------+-------------+-------------+--------------+--------------+

有关 'odd' 和 'even' 流的更多信息,请参阅 Scheme's SRFI 41 的基本原理。

我已将上面使用的代码添加到 GitHub