SICP 3.52 延迟 CDR

问题描述

Exercise 3.52,

(define sum 0)

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

;1: (define seq (stream-map accum (stream-enumerate-interval 1 20)))
;2: (define y (stream-filter even? seq))
;3: (define z (stream-filter (lambda (x) (= (remainder x 5) 0))
;                           seq))

;4: (stream-ref y 7)
;5: (display-stream z)

第 1 步: ;1: ==> (cons-stream 1 (stream-map proc (stream-cdr s))(假设只有当我们强制执行此流的 stream-cdr 时才会评估 cdr

sum 现在是 1

第 2 步: 1 不是偶数,因此(也被记住,因此不再添加),它调用 (stream-filter pred (stream-cdr stream))。 这将导致 cdr 的求值因此实现了 2is 偶数,因此它应该调用(cons-stream 2 (stream-cdr stream))

根据这个答案应该是 1+2 = 3 ,但它是 6

有人可以帮助解释为什么在调用当前 cdr 之前 carcdr 被具体化了吗?

解决方法

使用 Daniel P. Friedman's memoizing tail

#lang r5rs

(define-syntax cons-stream
  (syntax-rules () 
    ((_ h t) (cons h (lambda () t)))))

(define (stream-cdr s)
  (if (and (not (pair? (cdr s)))
           (not (null? (cdr s))))
      (set-cdr! s ((cdr s))))
  (cdr s))

我们观察到:

> sum
0
> (define seq (stream-map accum (stream-enumerate-interval 1 20)))
> sum
1
> seq
(mcons 1 #<procedure:friedmans-tail.rkt:21:26>)
> (define y (stream-filter even? seq))
> sum
6
> seq
(mcons
 1
 (mcons
  3
  (mcons 6 #<procedure:friedmans-tail.rkt:21:26>)))
> y
(mcons 6 #<procedure:friedmans-tail.rkt:21:26>)
> 

stream-filter? 需要到达它正在构造的流的第一个元素才能构造它。流的头部元素已经被强制、计算过,所以它必须已经存在。

在枚举区间1到20的累加和列表中,第一个偶数是6:

1      = 1
1+2    = 3
1+2+3  = 6
...