sicp-ex-4.4



<< Previous exercise (4.3) | Index | Next exercise (4.5) >>


meteorgan

  
  
 ;; a 
 ;; In eval, insert following code. 
  ((and? expr) (eval-and (and-clauses expr) env)) 
  ((or? expr) (eval-or (or-clauses expr) env)) 
  
 ;; In the interpreter, add those functions 
 (define (and? expr) (tagged-list? expr 'and)) 
 (define (and-clauses expr) (cdr expr)) 
 (define (eval-and exprs env) 
         (let ((v (evaln (first-exp exprs) env))) 
                 (cond ((last-exp? exprs)  
                            (if v v #f)) 
                           (else 
                                 (if v 
                                         (eval-and (rest-exps exprs) env) 
                                         #f))))) 
  
 (define (or? expr) 
         (tagged-list? expr 'or)) 
 (define (or-clauses expr) 
         (cdr expr)) 
 (define (eval-or exprs env) 
         (let ((v (evaln (first-exp exprs) env))) 
                 (cond ((last-exp? exprs) 
                            (if v v #f)) 
                           (else 
                                 (if v 
                                     v 
                                     (eval-or (rest-exps exprs) env)))))) 
  
 ;; b. you can use nested if implemented "and" and "or", like this: 
 ((and? expr) (evaln (and->if expr) env)) ;; add in eval 
  
 (define (and->if expr) 
         (expand-and-clauses (and-clauses expr))) 
 (define (expand-and-clauses clauses) 
         (if (null? clauses) 
             (make-if 'true 'true 'false)         
                 (let ((first (car clauses)) 
                           (rest (cdr clauses))) 
                    (if (null? rest)  
                            (make-if first first 'false) 
                        (make-if first (expand-and-clauses rest) 'false))))) 
  
 ((or? expr) (evaln (or->if expr) env))  ;; add in eval 
 (define (or->if expr) 
         (expand-or-clauses (or-clauses expr))) 
 (define (expand-or-clauses clauses) 
         (if (null? clauses) 
             (make-if 'true 'false 'true) 
                 (let ((first (car clauses)) 
                           (rest (cdr clauses))) 
                  (make-if first 'true (expand-or-clauses rest))))) 
  

I made a small change to meteorgan's answer. `or should return the value of the expression that evaluates to #t, not #t. Also I wonder in both answers wouldn't it be better to use (sequence->exp first) in case the predicate has more than 1 "action".


  
  
  
 (define (eval exp env) 
   (cond ((...)) 
         ((and? exp) (eval (and->if exp) env)) 
         ((or? exp) (eval (or->if exp) env)) 
         ((...)))) 
  
 (define (and? exp) (tagged-list? exp 'and)) 
 (define (and-operands exp) (cdr exp)) 
 (define (and->if exp) (expand-and-operands (and-operands exp))) 
 (define (expand-and-operands operands) 
   (if (null? operands) 'true 
       (make-if (car operands) 
                (expand-and-operands (cdr operands)) 
                'false))) 
  
 (define (or? exp) (tagged-list? exp 'or)) 
 (define (or-operands exp) (cdr exp)) 
 (define (or->if exp) (expand-or-operands (or-operands exp))) 
 (define (expand-or-operands operands) 
   (if (null? operands) 'false 
       (make-if (car operands) 
                'true 
                (expand-or-operands (cdr operands)))))