SICP 読み (55) 2.4.3 データ主導プログラミングと加法性
カゼひいたかも。熱っぽい。こんな時はそんなに出てないんでしょうが。
で、問題 2.76 ですが、前日エントリにある通り、微分手続きで試してみる。
問題 2.76
とりあえずメッセージパッシング流の微分手続きが以下。
(define (make-sum a1 a2)
(cond ((=number? a1 0) a2)
((=number? a2 0) a1)
((and (number? a1) (number? a2)) (+ a1 a2))
(else (list '+ a1 a2))))
(define (=number? exp num)
(and (number? exp) (= exp num)))
(define (make-product m1 m2)
(cond ((or (=number? m1 0) (=number? m2 0)) 0)
((=number? m1 1) m2)
((=number? m2 1) m1)
((and (number? m1) (number? m2)) (* m1 m2))
(else (list '* m1 m2))))
(define (make-from-operands l v)
(define (dispatch op)
(cond ((eq? op '+)
(make-sum (deriv (car l) v)
(deriv (cadr l) v)))
((eq? op '*)
(make-sum
(make-product (car l) (deriv (cadr l) v))
(make-product (deriv (car l) v) (cadr l))))
(else
(error "Unknown op -- MAKE-FROM-OPERANDS" op))))
dispatch)
(define (deriv exp var)
(cond ((number? exp) 0)
((variable? exp) (if (same-variable? exp var) 1 0))
(else (apply-generic (operator exp)
(make-from-operands (operands exp) var)))))
(define (apply-generic op arg) (arg op))
(define (operator exp) (car exp))
(define (operands exp) (cdr exp))
(define (variable? x) (symbol? x))
(define (same-variable? v1 v2)
(and (variable? v1) (variable? v2) (eq? v1 v2)))こんなカンジの試験にはパスしている。
("deriv test"
("deriv test (first)"
(assert-equal 0 (deriv '1 'x))
(assert-equal 0 (deriv 'y 'x))
(assert-equal 1 (deriv 'x 'x))
)
("deriv test (second)"
(assert-equal 1 (deriv '(+ x 3) 'x))
(assert-equal 'y (deriv '(* x y) 'x))
(assert-equal '(+ (* x y) (* y (+ x 3)))
(deriv '(* (* x y) (+ x 3)) 'x))
)
)やってみたら意外にカンタン。ちなみに「べき乗」の微分規則の追加もとても楽そげ。
で、やってみたのですが、盛り込んだのは
- make-exponentiation 手続きの追加
- べき乗の微分の手続きの追加
のみ。試験もすぐにパス。(問題 2.73 の試験と同じもの)
新しいデータ型が追加される、という部分においては、このメッセージパッシング方式が最適に見える。あるいは手続きについても同様なのかなぁ。でも、テキスト的な言い方をすれば「データ主導流」にも何かの利点はあるはずではないかと。
そういった意味では、データ主導流方式の利点は
- 手続きの実体が隠蔽されている
ということは大規模なものになればなるほど、演算の追加は煩雑かもしれないけれど、インターフェースがきちんと定義されていればあまり気を使う必要はないかも。そういった意味ではインターフェースが統一されているデータ主導流の方がポイント高いのか。
2.4 節はとても面白かった。