問題3.32
(define the-agenda (make-agenda)) (define a1 (make-wire)) (define a2 (make-wire)) (define output (make-wire)) (probe 'a1 a1) (probe 'a2 a2) (probe 'output output) (and-gate a1 a2 output) (set-signal! a1 0) (set-signal! a2 1) (propagate) (set-signal! a1 1) ;; ここで、a1 = 1, a2 = 1, output = 1 (set-signal! a2 0) ;; ここで、a1 = 1, a2 = 0, output = 0 (propagate) ;; 登録されるキューごとに、それが作られた時のinputのsignal状態がクロージャーとして記憶されているため、それが逆順で呼び出されると、 ;; 初めにactionが呼び出された時の環境でのoutputの状態が、最終的なoutputの状態として設定されてしまうため。 ;; ;; たとえば、and-gateではand-action-procedureの中で、get-signalで回線の状態をとって計算しnew-valueをつくり、after-delayの中でクロージャとして保存している。 (define (and-gate a1 a2 output) (define (and-action-procedure) (let ((new-value (logical-and (get-signal a1) (get-signal a2)))) ;; ココ! (after-delay and-gate-delay (lambda () (set-signal! output new-value))))) (add-action! a1 and-action-procedure) (add-action! a2 and-action-procedure) 'ok) ;; ;; ここで、 ;; a1が0。a2が1の状態で、 ;; (set-signal! a1 1) ;; (set-signal! a2 0) ;; とした場合、まず、 ;; (set-signal! a1 1) ;; この式を実行した時点では、a1もa2も1の状態なので、new-valueは1となりこの時点でクロージャが生成される、実行される関数では1がset-signal!される。 ;; そしてこの関数が初めに登録される。 ;; その後、 ;; (set-signal! a2 0) ;; が行われ、new-valueは0である。次の関数はこの0をset-signal!する。この関数が次に登録される。 ;; 'queue( (lambda () (set-signal! output 1)) (lambda () (set-signal! output 0)) ) ;; ここで、 ;; queueの呼び出し順序が逆転した場合、後の環境で作られたクロージャーが先に呼び出されることになる。 ;; つまり,(lambda () (set-signal! output 0))が初めに呼び出されてしまう。 ;; 後に呼び出されるのは、前の環境(a1=1,b1=1)で作られたクロージャーであり、(lambda () (set-signal! output 1))が呼び出される。 ;; a1 = 1, a2 = 0 であるにもかかわらず、outputが1となってしまい、and-gateの意図する動作とは変わってしまう。