問題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の意図する動作とは変わってしまう。