Exercise 4.11. Instead of representing a frame as a pair of lists, we can represent a frame as a list of bindings, where each binding is a name-value pair. Rewrite the environment operations to use this alternative representation. ———————————————————————————————————————————————————————————————————————— We use a non-empty list to represent the empty frame, so that we can mutate it to add bindings; since multiple references may exist to a frame (i.e. bound in procedures) it is not sufficient to return a new frame from add-binding-to-frame! (define (make-frame vars vals) (cons 'frame (map cons vars vals))) (define (frame-variables frame) (map car (cdr frame))) (define (frame-values frame) (map cdr (cdr frame))) (define (add-binding-to-frame! var val frame) (set-cdr! frame (cons (cons var val) (cdr frame)))) Lookup-variable-value will continue to work unchanged, though the lines (scan (frame-variables frame) (frame-values frame))))) will make it inefficient. We can define a better accessor function: (define (lookup-binding frame var) (assoc var (cdr frame))) And then change the environment operations to use this: (define (lookup-variable-value var env) (define (env-loop env) (if (eq? env the-empty-environment) (error "Unbound variable" var) (let* ((frame (first-frame env)) (binding (lookup-binding frame var))) (if binding (cdr binding) (env-loop (enclosing-environment env))))))) (define (set-variable-value! var val env) ... as above, but ... (if binding (set-cdr! binding val) ...)) (define (define-variable! var val env) (let* ((frame (first-frame env)) (binding (lookup-binding frame var))) (if binding (set-cdr! binding val) (add-binding-to-frame! var val frame))))