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))))
