SICP 読書ノート#39 - 3.5.4 ストリームと遅延評価 (pp.205-209)
(2015/09/15追記) SICPテキストのストリームでは問題3.77のテスト実行で返ってこないため、ここではracket/stream
版を使っています。ソースコードはGitHubに置いています。
- https://github.com/uents/sicp/blob/master/ch3/ch3.5.4.scm
- https://github.com/uents/sicp/blob/master/ch3/racket-streams.scm
前節で出てきた積分器integral
を使ってフィードバックループを持つ信号処理システムを考える。大学では制御工学を専攻していたのでこういうの大好きです。
このフィードバックシステムは以下のように実装される。
(define (solve f y0 dt) (define y (integral (delay dy) y0 dt)) (define dy (stream-map f y)) y)
積分器integral
の入力ストリームをdelay
しているところがミソ。現在時刻をt
とするとdy(t-1)
を入力する必要があるためこれで良い。
積分器は入力ストリームをforce
で評価する必要がある。
(define (integral delayed-integrand initial-value dt) (define int (cons-stream initial-value (let ((integrand (force delayed-integrand))) (add-streams (scale-stream integrand dt) int)))) int)
テスト。
racket@> (stream-ref (solve (lambda (y) y) 1 0.001) 1000) => 2.716923932235896
たぶんdt
の値を小さくするほど \( e \) に近づくはず。
問題 3.77
テキストで定義されているintegers-starting-from
に似たintegral
手続きを遅延リストに対応させよ。
(define (integral-ex delayed-integrand initial-value dt) (cons-stream initial-value (let ((integrand (force delayed-integrand))) (if (stream-null? integrand) the-empty-stream (integral-ex (stream-cdr integrand) (+ initial-value (* (stream-car integrand) dt)) dt))))) (define (solve-ex f y0 dt) (define y (integral-ex (delay dy) y0 dt)) (define dy (stream-map f y)) y)
テスト。先程の結果と同様。
(define (solve-ex f y0 dt) (define y (integral-ex (delay dy) y0 dt)) (define dy (stream-map f y)) y) ;;=> 2.716923932235896
問題 3.78
以下の二次微分方程式
\( \frac{d^2 y}{dt^2} - a \frac{dy}{dt} - by = 0\)
の解を求めるためのフィードバックシステムで、y
を求める手続きsolve-2nd
を実装せよ。
テキストの図のフィードバックシステムの通りに実装すればよいので、
(define (solve-2nd dy0 y0 a b dt) (define y (integral (delay dy) y0 dt)) (define dy (integral (delay (add-streams (scale-stream dy a) (scale-stream y b))) dy0 dt)) y)
テスト。
racket@> (stream-ref (solve-2nd 1 1 0 1 0.001) 1000) => 2.716923932235896 racket@> (stream-ref (solve-2nd 1 1 2 -1 0.001) 1000) => 2.716923932235896
問題 3.79
問題3.78をさらに汎用化して
\( \frac{d^2 y}{dt^2} = f( \frac{dy}{dt} , y ) \)
のフィードバックシステムにおいて、y
を求める手続きを実装する。
(define (solve-2nd-ex f y0 dy0 dt) (define y (integral (delay dy) y0 dt)) (define dy (integral (delay ddy) dy0 dt)) (define ddy (stream-map f dy y)) y)
テスト。f
を問題3.78と同じものを与えれば、当然結果も同じになる。
racket@> (stream-ref (solve-2nd-ex (lambda (dy y) y) 1 1 0.001) 1000) => 2.716923932235896 racket@> (stream-ref (solve-2nd-ex (lambda (dy y) (+ (* dy 2) (* y -1))) 1 1 0.001) 1000) => 2.716923932235896
問題 3.80
パス。対のストリームの課題だが§3.5.3で対のストリームを端折ってしまったので…
次回は「§3.5.5 関数プログラムの部品度とオブジェクトの部品化」から。