sicp-ex-2.30



<< Previous exercise (2.29) | Index | Next exercise (2.31) >>


jz

  
 (define my-tree (list 1 (list 2 (list 3 4) 5) (list 6 7))) 
  
 ;; Defining directly: 
 (define (square-tree tree) 
   (define nil '())  ;; my env lacks nil 
   (cond ((null? tree) nil) 
         ((not (pair? (car tree))) 
          (cons (square (car tree)) (square-tree (cdr tree)))) 
         (else (cons (square-tree (car tree)) 
                     (square-tree (cdr tree)))))) 
  
 ;; The above works, but there's no need to rip the tree apart in the (not 
 ;; ...) cond branch, since square-tree takes the tree apart for us in 
 ;; the else branch if we do one more recurse.  The following is 
 ;; better: 
 (define (sq-tree-2 tree) 
   (define nil '()) 
   (cond ((null? tree) nil) 
         ((not (pair? tree)) (square tree)) 
         (else (cons (sq-tree-2 (car tree)) 
                     (sq-tree-2 (cdr tree)))))) 
  
  
 ;; By using map: 
 (define (sq-tree-with-map tree) 
   (define nil '()) 
   (map (lambda (x) 
          (cond ((null? x) nil) 
                ((not (pair? x)) (square x)) 
                (else (sq-tree-with-map x)))) 
        tree)) 
  
 ;; Usage 
 (square-tree my-tree) 
 (sq-tree-2 my-tree) 
 (sq-tree-with-map my-tree) 
  
  
 ;; Originally, had the following for the else in sq-tree-with-map: 
 ;; 
 ;;   (else (cons (sq-tree-with-map (car x))  
 ;;               (sq-tree-with-map (cdr x)))))) 
 ;; 
 ;; defining the else clause this way raised an error: 
 ;; 
 ;;    The object 3, passed as an argument to map, is not a list. etc. 
 ;; 
 ;; my-tree had a primitive, 3, as one branch of the tree, so calling 
 ;; sq-tree-with-map eventually resolved to (sq-tree-with-map 3), which 
 ;; chokes.  Anyway, it was too much work, since all that was needed 
 ;; was a call to map *that* tree. 
  

bishboria

an alternative version of map but doesn't use cond and doesn't need to check for nil.

  
 (define (square-tree tree) 
   (map (lambda (sub-tree) 
          (if (pair? sub-tree) 
            (square-tree sub-tree) 
            (square sub-tree))) 
        tree)) 
  

meteorgan

This problem is similar to the example "scale-tree". so we can solve it as following:

  
  
 (define (square-tree1 tree) 
   (cond ((null? tree) '()) 
         ((not (pair? tree)) (* tree tree)) 
         (else (cons (square-tree1 (car tree)) 
                     (square-tree1 (cdr tree)))))) 
  
 (define (square-tree2 tree) 
   (map (lambda (x) 
                  (if (pair? x) 
                    (square-tree2 x) 
                    (* x x))) 
            tree)) 

master

In my opinion, mapping a complex procedure over a list kind of undermines the conceptual simplicity usually gained from using map. I think it's much clearer what's going on in the following solution:

 (define (square-tree tree) 
   (if (not (pair? tree)) 
       (square tree) 
       (map square-tree tree))) 

It's pretty much the same idea but inside-out, instead of making a decision as to whether to recurse while mapping, we make a decision as to whether to map while recursing.

...except your solution has a major flaw: it doesn't handle nil trees (i.e. empty lists. The map solutions implicitly handles nil trees, though.

And I would disagree with your general point about map. I think that pure "map" solutions operate at a higher level of abstraction: you're simply specifying a transformation to apply to elements of an input. It's very mathematical. Procedures are functions, after all, with domains and codomains, and map makes that relationship more explicit.