SICPを読む(109) 問題3.28 - 3.32 - ディジタル回路のシミュレータ

楽しいMIL記号の時間です。


加算器 - Wikipediaみたいなシミュレータを作っていきます。

問題 3.28

オアゲートを作れ。という問題。

(define (logical-or a b)
  (cond ((or (and (= a 1) (= b 1))
             (and (= a 0) (= b 1))
             (and (= a 1) (= b 0))) 1)
        ((and (= a 0) (= b 0) 0))
        (else
          (error "Invalid signal" s))))

(define (or-gate a1 a2 output)
  (let ((or-action-proc
          (lambda ()
            (let ((new-value (logical-or (get-signal a1)
                                         (get-signal a2))))
              (after-delay *or-gate-delay*
                           (lambda ()
                             (set-signal! output new-value)))))))
  (add-action! a1 or-action-proc)
  (add-action! a2 or-action-proc)
  'or-gate))

ふふ。回答は簡単なんだけど、ここまでのコーディングが結構めんどいぜ。


オアゲートが出来たので、半加算器を動かしてみる。

(define (harf-adder a b s c)
  (let ((d (make-wire))
        (e (make-wire)))
    (or-gate a b d)
    (and-gate a b c)
    (inverter c e)
    (and-gate d e s)))

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

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

(harf-adder a b s c)

(propagete) ; done

(probe 's s) ; s 5 New-value = 0
(probe 'c c) ; c 5 New-value = 1

1 + 1 = 10(2)

スゲー足し算した!!


Cで書くと、こんな感じ。

#include <stdio.h>

int main(void)
{
    int a = 1;
    int b = 1;

    printf("s = %d\n", (a | b) & ~(a & b)); /* a ^ b */
    printf("c = %d\n", a & b);

    return 0;
}

と、サックリ書けちゃうので、あんまりシミュレタ部分は重要では無くて、今回は、after-delayを使った遅延がポイントになりそうな感じ。

問題 3.29

inverterとand-gateを使って、or-gateを作る。

くらげ、くらげ。

(define (or-gate a b output)
  (let ((c (make-wire))
        (d (make-wire))
        (e (make-wire)))
    (inverter a c)
    (inverter b d)
    (and-gate c d e)
    (inverter e output)
    'or-gate))

最初,凄い図になったのは秘密。

全加算器

半加算器は1+1で、全加算器は1+1+1をする。つまり、最小が0,最大が3。

これおもろいな。

(define (full-adder a b c-in sum c-out)
  (let ((s (make-wire))
        (c1 (make-wire))
        (c2 (make-wire)))
    (harf-adder b c-in s c1)
    (harf-adder a s sum c2)
    (or-gate c1 c2 c-out)
    'full-adder))
  • 1+1をすると、10(2)になるので1を置くスペースが出来る。つまり、次に1を足してもキャリーは発生しない。
  • 1+0をすると、01(1)になるので、次はキャリーが発生するか、しないかわからない。
  • だから、キャリーをorすれば、1+1+1の演算の組合せを処理できる。おもろいな。

問題 3.30

繰上り伝播加算器を作れと。

list->number,number->listはいつも使ってる奴。

(define (number->list n . base)
  (letrec ((b (if (null? base) 10 (car base)))
           (iter (lambda (acc n)
                   (let ((q (quotient n b))
                         (m (cons (modulo n b) acc)))
                        (if (zero? q)
                            m
                            (iter m q))))))
          (iter '() n)))

(define (list->number l . base)
  (letrec ((b (if (null? base) 10 (car base)))
           (iter (lambda (l cont)
                  (if (null? l)
                      (cont 0 0)
                      (iter (cdr l) (lambda (sum k)
                                      (cont (+ (* (car l) (expt b k)) sum)
                                            (+ k 1))))))))
          (iter l (lambda (sum k) sum))))

(define (ripple-carry-adder a b)
  (letrec ((list->wire (lambda (n)
                         (map (lambda (x)
                                (let ((w (make-wire)))
                                      (set-signal! w x)
                                      w))
                              (number->list n 2))))
           (a-wires (list->wire a))
           (b-wires (list->wire b))
           (res (fold-right
                  (lambda (a b acc)
                    (let* ((sum (make-wire))
                           (carry (make-wire)))
                      (full-adder a b (car acc) sum carry)
                      (cons carry
                            (cons sum (cdr acc)))))
                  (list (make-wire))
                  a-wires
                  b-wires)))
    (propagete)
    (list->number (map get-signal res) 2)))

(ripple-carry-adder 12345678 11111111) ; 23456789

10進数→2進数→演算→10進数っと。

2進数にした時に、同じ桁になってないと桁落ちするのは仕様です。ホントは同じ桁になるようにゼロを補完しないとダメ。


一桁計算するのに、半加算器+半加算器+オアゲートが必要なので、n桁計算するにはn倍の計算が必要。しかも2進数なので・・・。

問題 3.31

新しいアクションを追加したのに、反映できてない事になる。つまり、アクションの連鎖が起こらないので、値が変わらない。

問題 3.32

順番どうりに進まないとおかしなことになることはわかるけど、イマイチこのシステムが掴みきれてないな。