sicp-ex-4.11



<< Previous exercise (4.10) | Index | Next exercise (4.12) >>


meteorgan

  
  
  
  
 (define (make-frame variables values) 
         (if (= (length variables) (length values)) 
                 (map cons variables values) 
                 (error "length mismatch -- MAKE-FRAME" variables values))) 
  
 (define (frame-variables frame) (map car frame)) 
 (define (frame-values frame) (map cdr frame)) 

Rptx

meteorgans answer does not work. You must change at least define-variable! and set-variable-value! for the system to work.


felix021 (a full version)

 ; 
 ; 
 ; 
 ; 
 ;SKIP(no change): 
 ;   enclosing-environment 
 ;   first-frame 
 ;   the-empty-environment 
  
 (define (make-frame variables values) 
     (cons  
         'table 
         (map cons variables values))) 
  
 (define (frame-pairs frame) (cdr frame)) 
  
 (define (add-binding-to-frame! var val frame) 
     (set-cdr! frame 
         (cons (cons var val) (frame-pairs frame)))) 
  
 ;SKIP: 
 ;   extend-environment 
  
 (define (lookup-variable-value var env) 
     (define (env-loop env) 
         (if (eq? env the-empty-environment) 
             (error "Unbound variable" var) 
             (let ((ret (assoc var (frame-pairs (first-frame env))))) 
                 (if ret 
                     (cdr ret) 
                     (env-loop (enclosing-environment env)))))) 
     (env-loop env)) 
  
 (define (set-variable-value! var val env) 
     (define (env-loop env) 
         (if (eq? env the-empty-environment) 
             (error "Unbound variables -- SET!" var) 
             (let ((ret (assoc var (frame-pairs (first-frame env))))) 
                 (if ret 
                     (set-cdr! ret val) 
                     (env-loop (enclosing-environment env)))))) 
     (env-loop env)) 
  
 (define (define-variable! var val env) 
     (let* ((frame (first-frame env)) 
            (ret (assoc var (frame-pairs frame)))) 
         (if ret 
             (set-cdr! ret val) 
             (add-binding-to-frame! var val frame)))) 
      

Yep -- ultimately, I went with this approach too of using an "associative list". Although interestingly, attempting to use the Scheme implementation of assoc did not work and had to use the book's approach!



mazj

A way not use associative list:

  
 (define (make-frame var val) (cons 'frame (map cons var val))) 
 (define (add-binding-to-frame! var val frame) 
     (set-cdr! frame (cons (cons var val) (cdr frame)))) 
 (define (set-var-to-frame! var val frame) 
     (let ((find #f)) 
         (set-cdr! frame 
               (map (lambda (pair)   
                         (if (eq? (car pair) var)  
                             (begin (set! find #t) (cons var val))  
                             pair))  
                    (cdr frame))) 
         find)) 
 (define (set-variable-value! var val env) 
     (define (env-loop env) 
         (if (eq? env the-empty-environment) 
             (error "Unbound variable -- SET!" var) 
             (if (set-var-to-frame! (first-frame frame)) 'done 
                 (env-loop (enclosing-environment env))))) 
     (env-loop env)) 
 (define (define-variable! var val env) 
     (let ((frame (first-frame env))) 
          (if (set-var-to-frame! var val frame) 'done 
              (add-binding-to-frame! var val frame)))) 

pvk

This is probably obvious to many, but only clicked for me while doing this exercise: set! is fundamentally different from set-car! and set-cdr!. (set! foo val) modifies the binding of the name foo: thus, any procedure of the form

 (define (add-binding-to-frame! var val frame)  
      (set! frame ...)) 

is useless, as it only redefines the binding of the procedure-local variable frame, not whatever frame we passed to the procedure. set-car! and set-cdr!, rather than just redefining a name, actually modify the pair that the name points to, and thus can be used in such contexts.