微分

scheme微分のプログラムで遊んでみました。

; 微分
(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))))
(define variable? symbol?)
(define (same-variable? x y) (eq? x y))
(define (make-sum a b) (list '+ a b))
(define (sum? exp) (equal? (car exp) '+))
(define (addend exp) (cadr exp))
(define (augend exp) (caddr exp))
(define (make-product a b) (list '* a b))
(define (product? exp) (equal? (car exp) '*))
(define (multiplier exp) (cadr exp))
(define (multiplicand exp) (caddr exp))
(deriv 5 'x)    ; => 0
(deriv 'x 'x)   ; => 1
(deriv 'y 'x)   ; => 0
(deriv (make-sum 'x 'x) 'x) ; => (+ 1 1)
(deriv (make-sum 'x 'y) 'x) ; => (+ 1 0)
(deriv (make-sum 'x 5) 'x)  ; => (+ 1 0)
(deriv (make-product 'x 'x) 'x) ; => (+ (* x 1) (* 1 x))
(deriv (make-product (make-sum 'x 5) (make-sum 'y 2)) 'x)
; => (+ (* (+ x 5) (+ 0 0)) (* (+ 1 0) (+ y 2))) ; うぅ、冗長だ・・・

;; 簡素化
(define (simplify exp)
    (cond
        ((number? exp) exp)
        ((variable? exp) exp)
        ((sum? exp)
            (let (
                (a (simplify (addend exp)))
                (b (simplify (augend exp))))
            (cond 
                ((eqv? a 0) b)
                ((eqv? b 0) a)
                ((and (number? a) (number? b)) (+ a b))
                (else exp))))
        ((product? exp)
            (let (
                (a (simplify (multiplier exp)))
                (b (simplify (multiplicand exp))))
            (cond 
                ((eqv? a 0) 0)
                ((eqv? b 0) 0)
                ((eqv? a 1) b)
                ((eqv? b 1) a)
                ((and (number? a) (number? b)) (* a b))
                (else exp))))
        (else exp)))
(simplify '(+ 0 0)) ; => 0
(simplify '(+ (* (+ x 5) (+ 0 0)) (* (+ 1 0) (+ y 2)))) ;=> (+ y 2)
(simplify (deriv (make-product (make-sum 'x 5) (make-sum 'y 2)) 'x)) ;=> (+ y 2) すっきり☆