3.5.1 Streams Are Delayed Lists

今までは"時間"の観点から現実世界をモデル化していたところを、別の視点からやってみましょう、というお話。具体的には(遅延)ストリームです。

前準備

サイトに載ってないコードだけ。

(define the-empty-stream '())

(define (stream-null? s)
  (eq? s the-empty-stream))

(define-syntax cons-stream
  (syntax-rules ()
    ((_ x y)
     (cons x (delay y)))))

; メモ化しない版
;(define-syntax delay
;  (syntax-rules ()
;    ((_ x)
;     (lambda () x))))

(define-syntax delay
  (syntax-rules ()
    ((_ x)
     (memo-proc (lambda () x)))))

3.50 (stream-map)

(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))))))

3.51

(define x (stream-map show (stream-enumerate-interval 0 10)))
(stream-ref x 5)
; => 1 2 3 4 5
(stream-ref x 7)
; => 6 7

必要になった分だけアクセスしている。かっこいー

3.52

(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) (zero? (remainder x 5))) seq))

これはdelayがメモ化する場合

(stream-ref y 7)
; => 136
(display-stream z)
; => 10 ; 15 ; 45 ; 55 ; 105 ; 120 ; 190 ; 210 ; done
sum
; => 210

となって、メモ化しない場合

(stream-ref y 7)
; => 162
(display-stream z)
; => 15 ; 180 ; 230 ; 305 ; done
sum
; => 362

となる。メモ化している方が自然な結果になっている。1本のストリームから複数のストリームに流れを渡すときに、いったん値が確定したところの値が変わっちゃいけない。set!による時間に依存してたらストリームにした意味がないもんな。