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>
こんな感じで結構簡約化できた。んが、それでも最後のやつはやっぱり複雑に見える。
簡約化は「何をもって単純とするか」、という目的によっても定義が変わってくるので難しい問題らしい。
ま、今は代数の話ではなくてプログラムの話なのでここまで!