2.73

练习 2.73 2.3.2节描述了一个执行符号求导的程序:

(define (deriv exp var)
    (cond ((number? exp) 0)
        ((variable? exp) (if (same-varialbe? 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。我们也可以将这一程序变换到数据导向的风格,将基本求导过程重新写成:

(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)请解释上面究竟做了些什么。为什么我们无法将相近的谓词number?和same-variable?也加入数据导向分派中?

b)请写出针对和式与积式的求导过程,并把它们安装到表格里,以便上面程序使用所需要的辅助性代码。

c)请选择一些你希望包括的求导规则,例如对乘幂(练习2.56)求导等等,并将它们安装到这一数据导向的系统里。

d)在这一简单的代数运算中,表达式的类型就是构造起它们来的代数运算符。假定我们想以另一种相反的方式做索引,使得deriv里完成分派的代码行像下面这样:

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

求导系统里还需要做哪些相应的改动?

a) 上面将对不同类型的导数求法放在一张表里,通过查表法来进行计算。大致建立了如下形式的表:

sum product
deriv deriv-sum deriv-product

number? 和 same-variable? 不能使用 (car expression) 对应到表中的类型,故不能放在表中。

b)

先复用一些已有函数:

(define variable? symbol?)
(define (same-variable? v1 v2)
  (and (variable? v1) (variable? v2) (eq? v1 v2))
)

(define (get op type)
    (cond
        ((and (eq? op 'deriv) (eq? type '+)) 'deriv-sum)
        (else 'deriv-product)
    )
)

(define (=number? v n)
  (if (number? v)
      (= v n)
      #f
      )
)

(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 addend cadr)
(define augend caddr)

以下是针对和式求导:

(define (deriv-sum exp var)
    (make-sum 
        (deriv (addend exp) var)
        (deriv (augend exp) var)
    )
)

(deriv-sum '(+ x y) 'x)

以下是针对积式求导:

(define (deriv-product exp var)
    (make-sum
        (make-product 
            (multiplier exp)
            (deriv (multiplicand exp) var)
        )
        (make-product
            (deriv (multiplier exp) var)
            (multiplicand exp)
        )
    )
)

(define multiplier cadr)
(define multiplicand caddr)

(define (make-product m1 m2)
  (cond
   ((=number? m1 1) m2)
   ((=number? m2 1) m1)
   ((=number? m1 0) 0)
   ((=number? m2 0) 0)
   ((and (number? m1) (number? m2)) (* m1 m2)) 
   (else (list m1 '* m2))
   )
)

(deriv-product '(* x x) 'x)

c) 将表格扩充一下:

sum product exp
deriv deriv-sum deriv-product deriv-exp

然后查表函数也需要相应地更新一下:

(define (get op type)
    (cond
        ((and (eq? op 'deriv) (eq? type '+)) deriv-sum)
        ((and (eq? op 'deriv) (eq? type '*)) deriv-product)
        (else deriv-exp)
    )
)
(define (deriv-exp expr var)
    (cond
        ((= (exponent expr) 0) 0)
        ((= (exponent expr) 1) 1)
        (else 
            (make-product
                (exponent expr)
                (make-exponentiation
                    (base expr)
                    (- (exponent expr) 1)
                )
            )
        )
    )
)

(define base cadr)
(define exponent caddr)

(define (make-exponentiation m1 m2)
    (list '** m1 m2)
)

; 期待得到 3x^2 的结果
(deriv-exp '(** x 3) 'x)

以上分别调用了 deriv-sum, deriv-product 和 deriv-exp,试一下直接调用 deriv,需要更新一下对 exp 的处理:

(define addend car)
(define augend cadr)

(deriv '(+ x 1) 'x)

d) 相反的方式索引,只是将表格换成了像下面这样:

deriv
sum deriv-sum
product deriv-product
exp deriv-exp

求导程序不需要做改动。

results matching ""

    No results matching ""