exercise 3.32 続き

とりあえず、試したコードでも張ってみるか
まずqueueの動作を変えたいので適当にやっとく

(define (front-ptr queue) (cdr queue))
(define (empty-queue? queue) (null? (front-ptr queue)))

(define (make-queue) (list '*queue*))

(define (front-queue queue)
  (if (empty-queue? queue)
      (error "FRONT called with an empty queue" queue)
      (car (front-ptr queue))))

(define (insert-queue! queue item)
  (set-cdr! queue (cons item (front-ptr queue))))

(define (delete-queue! queue)
  (cond ((empty-queue? queue)
         (error "DELETE! called with an empty queue" queue))
        (else
         (set-cdr! queue (cdr (front-ptr queue))))))

どうみてもキューじゃないので名前に違和感。


これを用いて以下を試す

(define a (make-wire))
(define b (make-wire))
(define c (make-wire))

(probe 'a a)
(probe 'b b)
(probe 'c c)

(and-gate a b c)
(set-signal! a 1)
(propagate)


(set-signal! b 1)
(set-signal! a 0)
(propagate)

まずはキューに変更無しバージョン

a 0  New-value = 0
                                                                                                                              
b 0  New-value = 0
                         
c 0  New-value = 0
                         
a 0  New-value = 1
                         
b 3  New-value = 1
                         
a 3  New-value = 0
                         
c 6  New-value = 1
                         
c 6  New-value = 0

続いて変更あり

a 0  New-value = 0
                         
b 0  New-value = 0
                         
c 0  New-value = 0
                         
a 0  New-value = 1
                         
b 3  New-value = 1
                         
a 3  New-value = 0
                         
c 6  New-value = 1

と言う風にcの値が変わっていると。
さて、ここで一度and-gateをみてみると

(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)

具体的にやっていることは
1. 2つのinputにand-actionを登録
2. アクションが呼ばれると、まずoutputを計算して(これが先)
3. outputの値をその値に変更する関数をagendaに登録

と言う感じ。2のアクションが呼ばれるのはwireの値が変わったときであり、キューとは関係ない。では実際に上のコードの結果を考えると

(define a (make-wire))
(define b (make-wire))
(define c (make-wire))

(probe 'a a)
(probe 'b b)
(probe 'c c)

;;a 0  New-value = 0
;;b 0  New-value = 0
;;c 0  New-value = 0

(and-gate a b c)
(set-signal! a 1)
;;-> a 0  New-value = 1 
(propagate)
;ここでa=1, b=0 キューは空になる。

(set-signal! b 1)                         
; -> b 3  New-value = 1 ;キューに関数追加(内容はcの値を1にする関数)関数はfとしておく
(set-signal! a 0)                         
; -> a 3  New-value = 0 ;キューに関数追加(内容はcの値を0にする関数)gとかにしとく

(propagate)                         
;;ここでキューの関数を呼んでいく。
;;呼ばれる順は g -> f の順なのでcの値は1になる。(gが呼ばれてもcの値は0のままなのでcのprobeは呼ばれない。)
; ->c 6  New-value = 1 ;fの変更によって呼び出された

なぜbが登録した関数がcを1にするかというとbの値を変更した時点ではaは1なので、cも1になる。
ちなみにこれは順番を変えたり、propagateの呼び出し順で結果が変わる。
あと、よく見るとキューの変更が無いバージョンでは、最後の部分が

c 6  New-value = 1
                         
c 6  New-value = 0

となってる。


まとめると、キューをLIFO(要するにスタックだよな・・・)にすると、実行の順序とかの影響をもろに受けちゃうのでダメってことでよいのかな。

しかし長いとよく分からん文章になっている気がする。
自分用のメモとはいえWebに公開するなら、もうすこし読みやすい文章を心がけたいなぁ