(sicp51)問題4.13

一度束縛した変数の束縛を取り除く構文make-unbound!を作る。
問題には、すべての環境から取り除くべきか、現在の環境のみから取り除くか選べと書いてあった。
現在の環境からのみ取り除く方がいいと考えてそのように実装した。
(現在の環境以外の束縛も取り除くと、クロージャの内部で値を取り除いたとき、独立しているはずの外部の変数も未束縛になってしまうため好ましくないと考えた。)
ちなみに、frameの表現形式は問題4.11のまま(var. value)という感じのpairにしている。

取り除く方法を見つける

テーブルから現在の要素を削除する方法を見つければいい。

(define env '('env))
(set-cdr! env (cons 2 (cdr env))); => #<undef>
(set-cdr! env (cons 1 (cdr env))); => #<undef>
(set-cdr! env (cons 2 (cdr env))); => #<undef>
env ;=>('env 2 1 2)
(define (search e l)
  (if (equal? e (cadr l))
      l
      (search e (cdr l))))

(let1 t (search 1 env)
  (set-cdr! t (cddr t)))
env ;=> ('env 2 2)

実際に定義したもの(一部)

上で作ったものと同様に考えてunbound-variable!を作成した。

(define (install-eval-package env)
  ;must call this procedure in setup-environment
  (define (define-action! name proc)    (put 'eval name proc))
  (define (eval&transform transformer)
    (lambda (exp env) (eval (transformer exp) env)))
....
  (define-action! 'make-unbound! (lambda (exp env)
                                   (unbound-variable! (cadr exp) env)
                                   'unbounded))
....
)
(define (unbound-variable! var env)
  (let* ((frame (first-frame env))
         (rest (let search ((l frame))
                 (cond ((null? (cdr l)) #f)
                       ((eq? var (caadr l))  l )
                       (else (search (cdr l)))))))
    (when rest
      (set-cdr! rest (cddr rest)))))

動作確認

xが未束縛になりerrorが発生している。

;;; M-Eval input:
(define x 3)

;;; M-Eval value:
ok

;;; M-Eval input:
(+ 2 x)

;;; M-Eval value:
5

;;; M-Eval input:
(make-unbound! x)

;;; M-Eval value:
unbounded

;;; M-Eval input:
(+ 2 x)
*** ERROR: Unbound variable x
Stack Trace:
_______________________________________
  0  (eval (first-operand exps) env)