Exercise 2.56 -- 2.58

Exercise 2.56

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.

Solution

; 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))))

Exercise 2.57

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.

Solution

; 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)))

Exercise 2.58

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.

  1. Show how to do this in order to differentiate algebraic expressions presented in infix form, such as (x + (3 * (x + (y + 2)))). To simplyfy the task, assume that + and * always take two arguments and that expressions are fully parenthesized.
  2. The problem becomes substantially harder if we allow standard algebraic notation, such as (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?

Solution

; 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