4.11 Exercise 4.11

One of the reasons that the approach using separate lists for variables and values in the environment worked so nicely is that a natural representation containing pointers to these lists fell out of it. If we imagine the bindings instead as a single list of binding pairs (where the value can itself also be a list), our data representation seems more elegant, but we lose that level of indirection that made the mutations easy to write. To deal with this, we can instead define a frame as having a head element in front, such that the cdr of this frame is a reliable pointer to the list of bindings.

(define the-empty-environment-2 '(head))
(define (lookup-variable-value-2 var env)
  (define (env-loop env)
    (define (scan bindings)
      (cond ((null? bindings) (env-loop (enclosing-environment env)))
            ((eq? var (caar bindings)) (cdar bindings))
            (else (scan (cdr bindings)))))
    (if (eq? env the-empty-environment-2)
        (error "Unbound variable" var)
        (let ((frame (first-frame env)))
          (scan (cdr frame)))))
  (env-loop env))
(define (extend-environment-2 bindings base-env)
  (cons (cons 'head bindings) base-env))
(define (add-binding-to-frame-2! var val frame)
  (if (null? (cdr frame))
      (set-cdr! frame (cons var val))
      (begin
        (set-cdr! (cdr frame) (cons (cadr frame) (cddr frame)))
        (set-car! (cdr frame) (cons var val)))))
(define (define-variable-2! var val env)
  (let ((frame (first-frame env)))
    (define (scan bindings)
      (cond ((null? bindings)
             (add-binding-to-frame-2! var val frame))
            ((eq? var (caar bindings))
             (set-car! (car bindings) val))
            (else (scan (cdr bindings)))))
    (scan (cdr frame))))
(define (set-variable-2! var val env)
  (define (env-loop env)
    (define (scan bindings)
      (cond ((null? bindings)
             (env-loop (enclosing-environment env)))
            ((eq? var (caar bindings))
             (set-cdr! (car bindings) val))
            (else (scan (cdr bindings)))))
    (if (eq? env the-empty-environment-2)
        (error "Unbound variable -- SET!" var)
        (let ((frame (first-frame env)))
          (scan (cdr frame)))))
  (env-loop env))