SICP 問題 2.56(x^n形式の微分方法を追加)

問題

更に多くの形の式が扱えるように微分プログラムを拡張する方法を示せ。deriv に新しい節を追加し、例えば


d(u^n)         du
─── = n・u^(n-1)・(──)
dx dx
微分規則を、適切な手続 exponentiation?, base, exponent, 及び make-exponentiation を定義して実装せよ。
(べき乗を表すのに記号 ** を使おう。)何かの 0 乗は 1 で、何かの 1 乗はそれ自身という規則を組み込め。

解答

まずは構成子を定義してみれば、各種選択子は自ずときまるわな。
というわけで make-exponentiation から順番に考えていこう。

;構成子
(define (make-exponentiation base exponent)
  ;簡約化も考慮にいれておくぜ。
  (cond ((=number? exponent 0) 1)
	((=number? exponent 1) base)
	(else (list '** base exponent))))

;述語
(define (exponentiation? exp)
  (and (pair? exp)
       (eq? (car exp) '**)))
      
;選択子(基数)
(define (base exp)
  (cadr exp))

;選択子(乗数)
(define (exponent exp)
  (caddr exp))

こんな感じか。一応動作確認を。


gosh> (define exp0 (make-exponentiation 'x 0))
exp0
gosh> (define exp1 (make-exponentiation 'x 1))
exp1
gosh> (define exp2 (make-exponentiation 'x 2))
exp2
gosh> (define exp3 (make-exponentiation 'x 3))
exp3
gosh> (define exp4 (make-exponentiation 'x 4))
exp4
gosh> exp0
1
gosh> exp1
x
gosh> exp2
(** x 2)
gosh> exp3
(** x 3)
gosh> exp4
(** x 4)
gosh> (exponentiation? exp0)
#f
gosh> (exponentiation? exp1)
#f
gosh> (exponentiation? exp2)
#t
gosh> (exponentiation? exp3)
#t
gosh> (exponentiation? exp4)
#t
gosh> (base exp0)

ERROR: pair required, but got 1

Stack Trace:
_______________________________________
gosh> (base exp1)

ERROR: pair required, but got x

Stack Trace:
_______________________________________
gosh> (base exp2)
x
gosh> (base exp3)
x
gosh> (base exp4)
x
gosh> (exponent exp0)

ERROR: pair required, but got 1

Stack Trace:
_______________________________________
gosh> (exponent exp1)

ERROR: pair required, but got x

Stack Trace:
_______________________________________
gosh> (exponent exp2)
2
gosh> (exponent exp3)
3
gosh> (exponent exp4)
4
gosh>

いいねぇ。

で、次にこいつを微分する規則だが、deriv に組み込まなきゃならんな。
現状の cond の節に一個追加するだけでいけそうだな。

(define (deriv exp var)
  (cond ((number? exp) 0)
	((variable? exp)
	 (if (same-variable? exp var) 1 0))
	((sum? exp)
	 (make-sum (deriv (addend exp) var)
		   (deriv (augend exp) var)))
	((product? exp)
	 (make-sum
	  (make-product (multiplier exp)
			(deriv (multiplicand exp) var))
	  (make-product (deriv (multiplier exp) var)
			(multiplicand exp))))
	;ここに新しい節を追加。
	((exponentiation? exp)
	 (let ((n (exponent exp)))
	   (cond ((= n 0) 1)
		 ((= n 1) n)
		 (else
		  (make-product n
				(make-exponentiation (base exp)
						     (- n 1)))))))
	(else
	 (error "unknown expression type -- DERIV" exp))))

こんな感じでいかがでしょう?乗数が 0 と 1 の時のケースも考慮しました。
では実験。


gosh> (deriv '(** x -1) 'x)
(* -1 (** x -2))
gosh> (deriv '(** x -1) 'x)
(* -1 (** x -2))
gosh> (deriv '(** x 0) 'x)
1
gosh> (deriv '(** x 1) 'x)
1
gosh> (deriv '(** x 2) 'x)
(* 2 x)
gosh> (deriv '(** x 3) 'x)
(* 3 (** x 2))
gosh> (deriv '(** x 4) 'x)
(* 4 (** x 3))
gosh>
おお〜、いいね〜。