問題2.73 – SICP(計算機プログラムの構造と解釈)その84

問題2.73

2.3.2 節の記号微分を行うプログラム。

(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))))
        ;; この部分にさらに規則を追加できる
        (else (error "unknown expression type -- DERIV" exp))))

(deriv '(+ 1 2 3) 'x)
gosh> 0
(deriv '(+ x y z) 'x)
gosh> 1
(deriv '(+ x 3) 'x)
gosh> 1
(deriv '(+ (+ x x x) (+ x x x)) 'x)
gosh> 6
(deriv '(* x y) 'x)
gosh> y
(deriv '(* (* x y) (+ x 3)) 'x)
gosh> (+ (* x y) (* y (+ x 3)))

データ主導プログラミングに修正したもの。

(define (deriv exp var)
  (cond ((number? exp) 0)
        ((variable? exp) (if (same-variable? exp var) 1 0))
        (else ((get 'deriv (operator exp)) (operands exp)
                                           var))))

(define (operator exp) (car exp))

(define (operands exp) (cdr exp))

問題 a.

最初のプログラムとの違いは以下の部分のみ。

((get 'deriv (operator exp)) (operands exp) var)

型である演算子記号(+, *)と演算である deriv から、対応する演算を選び実行する。

number?variable? は引数がリストでないために型を持たないから、データ主導の振り分けに組み込めない。

 
+ *
演算 deriv deriv-sum deriv-product
make make-sum make-product

問題b.

和の微分
(define (install-sum-package)
  ;; 内部手続き
  (define (addend s) (car s))
  (define (augend s)
    (if (null? (cddr s))
        (cadr s)
        (cons '+ (cdr s))))
  (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 (deriv-sum exp var)
    (make-sum (deriv (addend exp) var)
              (deriv (augend exp) var)))
  ;; システムの他の部分とのインターフェース
  (put 'make '+ make-sum)
  (put 'deriv '+ deriv-sum)
  'done)

(install-sum-package)
gosh> done
(deriv '(+ 1 2 3) 'x)
gosh> 0
(deriv '(+ x y z) 'x)
gosh> 1
(deriv '(+ x 3) 'x)
gosh> 1
(deriv '(+ (+ x x x) (+ x x x)) 'x)
gosh> 6
積の微分
(define (install-product-package)
  ;; 内部手続き
  (define (multiplier p) (car p))
  (define (multiplicand p)
    (if (null? (cddr p))
        (cadr p)
        (cons '* (cdr p))))
  (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 (deriv-product exp var)
    ((get 'make '+)
      (make-product (multiplier exp)
                    (deriv (multiplicand exp) var))
      (make-product (deriv (multiplier exp) var)
                    (multiplicand exp))))
  ;; 他の部分とのインターフェース
  (put 'make '* make-product)
  (put 'deriv '* deriv-product)
  'done)

(install-product-package)
gosh> done
(deriv '(* x y) 'x)
gosh> y
(deriv '(* (* x y) (+ x 3)) 'x)
gosh> (+ (* x y) (* y (+ x 3)))

問題c.

べき乗の微分
(define (install-exponent-package)
  ;; 内部手続き
  (define (base s) (car s))
  (define (exponent s) (cadr s))
  (define (make-exponentiation b e)
    (cond ((=number? e 0) 1)
          ((=number? e 1) b)
          (else (list '** b e))))
  (define (deriv-exponentation exp var)
    (let ((make-p (get 'make '*)))
         (make-p
           (make-p
             (exponent exp)
             (make-exponentiation (base exp) (- (exponent exp) 1)))
           (deriv (base exp) var))))
  ;; 他の部分とのインターフェース
  (put 'make '** make-exponentiation)
  (put 'deriv '** deriv-exponentation)
  'done)

(install-exponent-package)
gosh> done
(deriv '(** x 2) 'x)
gosh> (* 2 x)
(deriv '(** x 1) 'x)
gosh> 1
(deriv '(** x 0) 'x)
gosh> 0

問題d.

各手続きの put の引数の"演算"と"型"を逆転すればよい。

計算機プログラムの構造と解釈
ジェラルド・ジェイ サスマン ジュリー サスマン ハロルド エイブルソン
ピアソンエデュケーション
売り上げランキング: 6542
«
»