sicp-ex-2.57


 (define (make-sum-list l) 
   (if (= (length l) 2) 
       (list '+ (car l) (cadr l)) 
       (make-sum (car l) (make-sum-list (cdr l))))) 
 (define (make-sum a1 a2) 
   (cond ((=number? a1 0) a2) 
         ((=number? a2 0) a1) 
         ((and (number? a1) (number? a2)) (+ a1 a2)) 
         (else (make-sum-list (list a1 a2))))) 
  
 (define (make-product-list l) 
   (if (= (length l) 2) 
       (list '* (car l) (cadr l)) 
       (make-product (car l) (make-product-list (cdr l))))) 
 (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 (make-product-list (list m1 m2))))) 
  
 (define (augend s) 
   (let ((a (cddr s))) 
     (if (= (length a) 1) 
         (car a) 
         (make-sum-list a)))) 
 (define (multiplicand p) 
   (let ((m (cddr p))) 
     (if (= (length m) 1) 
         (car m) 
         (make-product-list m)))) 
  
 ;; tests 
 (deriv '(* (* x y) (+ x 3)) 'x) 
 ;; (+ (* x y) (* y (+ x 3))) 
  
 (deriv '(* x y (+ x 3)) 'x) 
 ;; (+ (* x y) (* y (+ x 3))) 

<< Previous exercise (2.56) | Index | Next exercise (2.58) >>


NTeGrotenhuis

Extend the differentiation program to handle sums and products of arbitrary numbers of (two or more) terms. Then the last example above could be expressed as

(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.

See sicp-ex-2.56 for the differentiation program

All that must be done to solve this problem is to change augend and multiplicand so that they return the sum or product, respectively, of the remaining items in the list.

 (define (augend s)    
   (accumulate make-sum 0 (cddr s))) 
  
 (define (multiplicand p)  
   (accumulate make-product 1 (cddr  p))) 

This works because (accumulate is really awesome.

 (define (accumulate op initial sequence) 
   (if (null? sequence) 
       initial 
       (op (car sequence) 
           (accumulate op initial (cdr sequence))))) 

It recursively applies the make function to add up all the items in the list.

 (accumulate make-sum 0 (cddr s))

Is analogous to

 (accumulate +  0 (cddr s))

For symbolic data. We must use (cddr s)) to get the rest of the list begining with the 3rd item.


meteorgan

As is mentioned above. All that must be done is to change "augend" and "multiplicand". The above solution is interesting, it use "accumulate" to change the representation of sum and product into the former version. for example: (augend (+ x x x x)) is (+ x (+ x x)), (multiplicand (* x x x x)) is (* x (* x x)). But there is another solution. Here is my code:

 ;; if there is 4th item, make-sum 3rd item and 4th item 
 (define (augend expr) 
   (if (null? (cdddr expr)) 
         (caddr expr) 
         (make-sum (caddr expr) (cadddr expr)))) 
  
 ;; if there is 4th item, make-product 3rd item and 4th item 
 (define (multiplicand expr) 
   (let ((first (caddr expr)) 
         (rest (cdddr expr))) 
     (if (null? rest) 
         first 
         (make-product first (cadddr expr))))) 

AMS

I agree with meteorgan's comment about the use of accumulate. As awesome and simple as it is, it changes the representation away from what we are using. I too came up with similar implementations of "augend" and "multiplicand" with the following changes. Instead of using "make-sum" and "make-product" I simply cons the operator to the beginning of the list.

 (define (augend s) 
   (if (null? (cdddr s)) 
       (caddr s) 
       (cons '+ (cddr s)))) 
  
 (define (multiplicand p) 
   (if (null? (cdddr p)) 
       (caddr p) 
       (cons '* (cddr p)))) 
  
 ;; using lets might make it easier to understand 
 (define (augend s) 
   (let ((augend-element (cddr s))) 
     (if (null? (cdr augend-element)) 
         (car augend-element) 
         (cons '+ augend-element)))) 
  
 (define (multiplicand p) 
   (let ((multiplicand-element (cddr p))) 
     (if (null? (cdr multiplicand-element)) 
         (car multiplicand-element) 
         (cons '* multiplicand-element)))) 

AThird

Here's my attempt at this.

  
 (define (augend s)  
   (if (> (length s) 3) 
       (make-sum (addend (cdr s)) (augend (cdr s))) 
       (caddr s))) 
  
 (define (multiplicand p) 
   (if (> (length p) 3) 
       (make-product (multiplier (cdr p)) 
                     (multiplicand (cdr p))) 
       (caddr p)))