<< Previous exercise (4.12) | Index | Next exercise (4.14) >>


 ;; unbound variable in current frame 
 (define (unbound? expr) (tagged-list? expr 'unbound)) 
 (define (unbind-variable expr env) (make-unbound (cadr expr) env)) 
 (define (make-unbound variable env) 
         (let ((vars (frame-variables (first-frame env))) 
                   (vals (frame-values (first-frame env)))) 
                 (define (unbound vars vals new-vars new-vals) 
                         (cond ((null? vars) 
                                    (error "variable is not in the environment -- MAKE-UNBOUND"      
                                  ((eq? (car vars) variable) 
                                   (set-car! env  
                                                 (cons (append new-vars (cdr vars))  
                                                          (append new-vals (cdr vals))))) 
                                  (else (unbound (cdr vars) (cdr vals)  
                                                           (cons (car vars) new-vars)  
                                                           (cons (car vals) new-vals))))) 
                 (unbound vars vals '() '()))) 
 ;; add this in eval 
 ((unbound? expr) (unbind-variable expr env)) 
 ;;there's no need to unbound bindings in the enclosing environments,and no need to send an error message(just like "define"),a simpler version is: 
 (define (make-unbound! var env) 
   (let* ((frame (first-frame env)) 
          (vars (frame-variables frame)) 
          (vals (frame-values frame))) 
     (define (scan pre-vars pre-vals vars vals) 
       (if (not (null? vars)) 
           (if (eq? var (car vars)) 
               (begin (set-cdr! pre-vars (cdr vars)) 
                      (set-cdr! pre-vals (cdr vals))) 
               (scan vars vals (cdr vars) (cdr vals))))) 
     (if (not (null? vars)) 
         (if (eq? var (car vars)) 
             (begin (set-car! frame (cdr vars)) 
                    (set-cdr! frame (cdr vals))) 
             (scan vars vals (cdr vars) (cdr vals)))))) 

wing's answer is more 4 times quick than my:

 (define (make-unbound! var env) ;4*linear 
     (let ((frame (first-frame env))) 
         (define pairs  
             (filter (lambda (pair) (not (eq? (car pair) var-target))) 
                     (map cons (car frame) (cdr frame)))) 
         (set-car! frame (map car pairs)) 
         (set-cdr! frame (map cdr pairs)))) 


This isn't that elegant. The problem is that the way I wrote search-frame it returns the exact cons cell where the match occurred, so there really isn't any way to access the surrounding frame. Could easily be picked up by a garbage collector though.

 (define (make-unbound! var env) 
   (let ((frame (first-frame env))) 
     (search-frame var 
                   (lambda (res) (and res (set-car! res '()) (set-cdr! res '()))) 
                   (lambda () '())))) 


I believe 'make-unbound!' should construct a list for the evaluator to check with 'unbound?', and only afterwards it calls the unbinding procedure:

 (define (make-unbound! var)            
   (list 'unbound! var))                
 (define (unbound? exp) (tagged-list? exp 'unbound!)) 
 (define (unbound!-var exp)              
   (cadr exp))                           
 (define (unbound! var env)                                
   (define (remove-binding var bindings) 
     (cond ((null? bindings) '()) 
           ((eq? var (caar bindings)) (cdr bindings)) 
           (else (cons (car bindings)  
                       (remove-binding var (cdr bindings)))))) 
   (let ((frame (first-frame env))) 
     (set-cdr! frame (remove-binding var (frame-bindings frame)))))