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 節はとても面白かった。