4.12 Exercise 4.12
If we generalize lookup-variable-value, set-variable-value!,
and define-variable! fully, we can see that the common pattern
in each is that we loop through one frame in the environment, running
one procedure if we find a binding with a given variable name, and
running a different procedure if we reach the end of the current frame
without finding it. In the case of lookup-variable-value and
set-variable-value!, the latter procedure is the same iterative
loop, but it doesn’t need to be so.
Based on the behaviors of these procedures, we can (somewhat
arbitrarily) decide that the procedure executed if we find a
binding should take the lists of variables and values as its
parameters, and that the procedure to be performed if the
frame doesn’t contain the binding we’re looking for only
needs to take the current environment.
(define (search-env if-found if-not-found) | (lambda (var env) | (define (scan vars vals) | (cond ((null? vars) (if-not-found env)) | ((eq? var (car vars)) (if-found vars vals)) | (else (scan (cdr vars) (cdr vals))))) | (if (eq? env the-empty-environment) | (error "Unbound variable" var) | (let ((frame (first-frame env))) | (scan (frame-variables frame) | (frame-values frame)))))) |
|
(define (lookup-variable-value-alt val env) | ((search-env | (lambda (vars vals) (car vals)) | (lambda (env) (lookup-variable-value-alt val (enclosing-environment env)))) | val env)) |
|
(define (set-variable-value-alt! var val env) | ((search-env | (lambda (vars vals) (set-car! vals val)) | (lambda (env) (set-variable-value-alt! var val (enclosing-environment env)))) | var env)) |
|
(define (define-variable!-alt var val env) | ((search-env | (lambda (vars vals) (set-car! vals val)) | (lambda (env) (add-binding-to-frame! var val (first-frame env)))) | var env)) |
|
This arguably makes it clearer what the semantics of these
procedures are – in particular, whether they search beyond
the first frame of the environment or not.