SICP §2.3.2(記号微分 その2 [代数式の表現])
前エントリで羅列した、微分を行う為の各種手続きを定義していく。
(define (variable? x) (symbol? x)) (define (same-variable? v1 v2) (and (variable? v1) (variable? v2) (eq? v1 v2))) (define (sum? x) (and (pair? x) (eq? (car x) '+))) (define (addend s) (cadr s)) (define (augend s) ;(cddr s) ではないことに注意。 ;構成子(make-sum 手続)がどう表現されるかにもよるが、 ;リストの後半という扱いではなく、 ;「演算子、加数の次にある要素を取り出す」という意図で、 ;caddr が使用されているっぽ。 (caddr s)) (define (make-sum a1 a2) (list '+ a1 a2)) (define (product? x) (and (pair? x) (eq? (car x) '*))) (define (multiplier p) (cadr p)) (define (multiplicand p) (caddr p)) (define (make-product m1 m2) (list '* m1 m2))
これで、とりあえず多項式?の微分形式を求めることができる用意ができたので、SICPの流れに沿って確認してみる。
gosh> (deriv '(+ x 3) 'x)
(+ 1 0)
gosh> (deriv '(* x y) 'x)
(+ (* x 0) (* 1 y))
gosh> (deriv '(* (* x y) (+ x 3)) 'x)
(+ (* (* x y) (+ 1 0)) (* (+ (* x 0) (* 1 y)) (+ x 3)))
gosh>
よしよし。
で、簡約化されてないので、make-sum 手続と make-product 手続を次のように変更する。
;make-sum 手続を次のように変更。 (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)))) ;式 exp が与えられた数 num に等しいかを確認する手続 (define (=number? exp num) (and (number? exp) (= exp num))) ;make-product 手続を次のように変更。 (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))))
で、さっきの微分の例をもう一度。
こんな感じで結構簡約化できた。んが、それでも最後のやつはやっぱり複雑に見える。
gosh> (deriv '(+ x 3) 'x)
1
gosh> (deriv '(* x y) 'x)
y
gosh> (deriv '(* (* x y) (+ x 3)) 'x)
(+ (* x y) (* y (+ x 3)))
gosh>
簡約化は「何をもって単純とするか」、という目的によっても定義が変わってくるので難しい問題らしい。
ま、今は代数の話ではなくてプログラムの話なのでここまで!