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