Show how to extend the basic differentiator to handle more kinds of expressions. For instance, implement the differentiation rule
by adding a new clause to the deriv
program and defining appropriate
procedures exponentiation?
, base
, exponent
, and
make-exponentiation
. (You may use the symbol **
to denote
exponentiation.) Build in the rules that anything raised to the power
0 is 1 and anything raised to the power 1 is the thing itself.
; exponentiation
(define (make-exponentiation base exponent)
(cond ((=number? exponent 0) 1)
((=number? exponent 1) base)
((and (number? base) (number? exponent)) (expt base exponent))
(else (list '** base exponent))))
(define (exponentiation? x)
(and (pair? x) (eq? (car x) '**)))
(define (base x) (cadr x))
(define (exponent x) (caddr x))
; derivative
(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))))
((and (exponentiation? exp) (number? (exponent exp)))
(make-product
(exponent exp)
(make-product
(make-exponentiation (base exp) (- (exponent exp) 1))
(deriv (base exp) var))))
(else
(error "unknown expression type - DERIV" exp))))
Extend differentiation program to handle sums and products of arbitrary numbers of two or more terms. Then the two following procedures would be equivalent:
(deriv '(* (* x y) (+ x 3)) 'x)
(deriv '(* x y (+ x 3)) 'x)
Try to do this by changing only the representation for sums and
products, without changing the deriv
procedure at all. For example,
the addend
of a sum would be the first term, and the augend
would
be the sum of the rest of the terms.
; sum
(define (make-sum . a)
(if (< (length a) 2)
(error "make-product requires at least 2 arguments")
(let ((num (fold + 0 (filter number? a)))
(var (filter symbol? a))
(expr (filter pair? a)))
(let ((sum
(filter (lambda (x) (not (or (null? x) (=number? x 0))))
(cons num (append var expr)))))
(cond ((= (length sum) 0) 0)
((= (length sum) 1) (car sum))
(else (cons '+ sum)))))))
(define (sum? x)
(and (pair? x) (eq? (car x) '+) (> (length x) 2)))
(define (addend s) (cadr s))
(define (augend s)
(if (> (length s) 3) (cons '+ (cddr s)) (caddr s)))
; product
(define (make-product . m)
(if (< (length m) 2)
(error "make-product requires at least 2 arguments")
(let ((num (fold * 1 (filter number? m)))
(var (filter variable? m))
(expr (filter pair? m)))
(let ((prod
(filter (lambda (x) (not (or (null? x) (=number? x 1))))
(cons num (append var expr)))))
(if (< (length prod) 2)
(car prod)
(cond ((=number? (car prod) 0) 0)
((=number? (car prod) 1) (cons '* (cdr prod)))
(else (cons '* prod))))))))
(define (product? x)
(and (pair? x) (eq? (car x) '*) (> (length x) 2)))
(define (multiplier p) (cadr p))
(define (multiplicand p)
(if (> (length p) 3) (cons '* (cddr p)) (caddr p)))
Suppose we want to modify the differentiation program so that it works
with ordinary mathematical notation, which +
and *
are infix
rather than prefix operators. Since the differentiation program is
defined in terms of abstract data, we can modify it to work with
different representations of expressions solely by changing the
predicates, selectors, and constructors that define the representation
of the algebraic expressions on which the differentiator is to operate.
(x + (3 * (x + (y + 2))))
. To
simplyfy the task, assume that +
and *
always take two
arguments and that expressions are fully parenthesized.(x + 3 * (x + y + 2))
, which drops
unnecessary parentheses and assumes that multiplication is done
before addtion. Can you design appropriate predicates, selectors,
and constructors for this notation such that our derivative program
still works?; if operatior have only two arguments and are fully parenthesized
(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 (sum? x)
(and (pair? x) (= (length x) 3) (eq? (cadr x) '+)))
(define (addend s) (car s))
(define (augend s) (caddr s))
(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 (product? x)
(and (pair? x) (= (length x) 3) (eq? (cadr x) '*)))
(define (multiplier p) (car p))
(define (multiplicand p) (caddr p))
; if standard algebraic conventions are allowed
; only predicate and selectors need to modified (?), constructors are
; same as above. Although the derive procedure needs to be changed so
; that it test for products before sums
; memp, like memq -- returns the complement of memq, so that
; (append (memp item list) (memq item list)) returns list if item is
; in list. But will also return list if item is not in list
(define (memp item x)
(cond ((null? x) x)
((eq? item (car x)) '())
(else (cons (car x) (memp item (cdr x))))))
(define (sum? x)
(and (pair? x) (> (length x) 2) (pair? (memq '+ x))))
(define (addend s)
(let ((a (cdr (memp '+ s))))
(if (= (length a) 1) (car a) a))
(define (augend s)
(let ((a (cdr (memq '+ s))))
(if (= (length a) 1) (car a) a)))
(define (product? x)
(and (pair? x) (> (length x) 2) (pair? (memq '* x))))
(define (multiplier p)
(let ((m (cdr (memq '* p))))
(if (= (length m) 1) (car m) m)))
(define (multiplicand p)
(let ((m (cdr (memq '* p))))
(if (= (length m) 1) (car m) m)))
; NB: not yet tested with deriv