<< Previous exercise (4.10) | Index | Next exercise (4.12) >>
meteorgans answer does not work. You must change at least define-variable! and set-variable-value! for the system to work.
; ; ; ; ;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!
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))))
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.
meteorgan