(apply-generic op . args)

教育者, 将軍, 栄養士, 心理学者, 親はプログラムする. 軍隊, 学生, 一部の社会はプログラムされる. - 計算機プログラムの構造と解釈 序文

初めてのGo言語 読了

Golangの勉強をしようと思い、「初めてのGo言語」を読みました。

2023/05/01から読み始め、2023/06/12に読み終わりました。 やってない日もあるので、実際には34日間かかりました。

この本を選んだ理由

以下のレビューがとても刺さったので(特に実用〜とジェネリクスのくだり)。

初学者向けによく整理され整然と順序立てて説明されているうえ豊富な実践的プラクティスが自然に織り込まれており応用書を別途読む必要のない非常に完成度の高い入門書に仕上がっている。本書の数ヶ月前に刊行された実用Go言語はほとんど重複した内容の応用書でありジェネリクスにも対応していないため完全に無用の長物となった。

https://www.amazon.co.jp/gp/customer-reviews/R2KVM4LHU48X18/ref=cm_cr_dp_d_rvw_ttl?ie=UTF8&ASIN=4814400047

良かった点

既に他言語でプログラミングをしているプログラマにとって、この本でGo言語を1から学ぶのはとても良いと思いました。 副題の「他言語プログラマーのためのイディオマティックGo実践ガイド」というのがまさに、と思いました。

オライリーのタイトルあるあるですが、「初めてのGo言語」であって「初めてのプログラミングをGoで学ぶ」ではありませんので、初学者が読む本では無いのはいつも通りだと思います。

自分は基本的にRubyで仕事をしており、今まで真剣に学んだ言語はRubyLispなどだったのでGoのような言語をきちんと学ぶのは初めてでしたが特に混乱もなく読み進められました。

物足りなかった点

SICPのように練習問題があれば良かったと思いました。 またSICPのように、1冊の本を通して何か大きなものを作るというプログラミングという行為があれば更に良かったです。

更にSICPのように(以下略

ポインタとGCについて

ポインタやGCについてはSICPで既に知見があったので、「あ〜これSICPでやったわー」ってなりましたが、の辺りを一切知らないLL系言語の人がこの本でその辺をちゃんと理解出来るのか?はちょっと解らないです。

今後

今後は簡単なプログラムを気の向くままGoでいくつか書いてみて、満足したらGo言語でつくるインタプリタをやる予定です。

SICP 読了

計算機プログラムの構造と解釈(以下SICP)を読み切りました。 2022年9月22日から2023年4月30日まで、157日間かかりました。

読み終わりましたが「やりきった」「理解しきった」とは、口が裂けても言えません。 全体の難しさや濃淡を考えなかった場合、なんとか70%くらいの理解度だと思います(章や節の難易度や濃さを考慮すると40%くらい...?).

けどpassした問題もありますが、結構ちゃんとガッツリ問題は解きました。

1. SICPと私

1.1 12年前

SICPは自分にとって特別な書籍です。 25歳の頃に一度勉強会形式でチャレンジしたものの、全く付いて行けずに当時の自分がいかに知識も技術もないのか教えてくれて感謝しています。

実は「自分は一生かかってもSICPを読み切る事が出来ないのではないか」と思っていました。 それは地頭的な意味合いで、自分にはこれを理解したり解いたりするのは無理なのではないか?と。

1.1.1 SICPへの信仰

SICPにはそういった、ある種の畏敬の念を抱いていました。 それは大自然への信仰にも近い想いや思想と言えます(SICPは海というよりは山だと思うので山岳信仰に近いと思います(???))。

LISP を勉強しておきたいのは、別の理由からです――それをモノにしたときにすばらしい悟り体験が得られるのです。この体験は、その後の人生でよりよいプログラマーとなる手助けとなるはずです。たとえ、実際には LISP そのものをあまり使わなくても。

https://cruel.org/freeware/hacker.html

1.2 12年後

しかしあれから12年、久しぶりにSICPを手に取り、ざっとななめ読みしてみると今なら行ける気がしました。 あのSICPを自分が(完璧じゃなくても)最後まで読めるかもしれない...

完璧な理解は求めない(2週目3週目で少しずつ理解度を上げる)
無理せず1日1P,1問を目処に継続してやり切る事が目標

この2つを指針にやり始め、遂に157日かけて読み切りました.

大切なことなので2回目になりますが、「やりきった」「理解しきった」とは、口が裂けても言えません

2. 感想

2.1 良かった点

2.1.1 (not 技術の話)

まずは技術的な点よりも最終章である5章辺りに入って終わりが見えてきてから、「あのSICPをここまで読んだ」という自信がついた事でしょうか。

この辺りからSICPと同時進行で英語を勉強し始め、昔に挫折したギターをやり始めました。

「あのSICPを5章まで来たんだから、SICPは中学英語より500倍(雑)難しいんだからやれるやろ」 「ギター弾けるようになる割合よりも、SICP挫折する割合の方が高い。今の俺ならギターだって弾けるようになる」

という謎の自信がつきました。

後は「無理せず1日1P,1問を目処に継続してやり切る事が目標」というのが良かったです。 これは英語やギターにも役立っています。

技術書はその時期の気分で読んだり読まなかったりしていましたが、引き続き毎日技術書をやっていこうと思います。

2.1.2 (eq 技術の話)

lispそのものや再帰等々、いわゆるSICPで最初に躓きがちな辺りは以前やってたので特に問題なかったです。

ポインタ、環境とフレーム、この辺りは理解が浅かったので勉強していて凄い楽しかったです。 後はストリーム、これは自分的に凄い良かったです。 実務でも役に立ちまくっています。

あとはやはり4章と5章は勉強になりました。 理解は浅い自覚がありますが...

特に5章はもう一度やりなおしたいな。

2.2 良くなかった点

章の途中の問題、これが「まだ動かせないけどこの様に修正せよ」というのが多くて辛かった... 今までやってきた技術書でこの形式は初めてだったと思います。

途中からググって、過去の先人が残したプログラムを持ってきて問題のソースコードを動かせるようにして問題を解きました。 が、これ本当ならそれが出来ない状態で頭の中で考えてやるんですよね? 凄すぎでしょう...

3. SICPと私の今後について

4章から理解度が急激に浅くなっています。

難しさよりも100日を超えた辺りからの「飽き」が原因です。 ここで辞めたら二度と再開しない気がする、と思って気合で読みましたが途中からもう飽きてきていて...

それでも通しで読めたのは大きかったです。 また気分が向いたら本を開いて「あーここ理解浅いからやろう」みたいにやっていきたいと思います。

今はそんな気になれませんが、更に気が向いたらまた通読してみる事もあるかもしれません。

4. 最後に

教育者, 将軍,栄養士, 心理学者, 親はプログラムする。軍隊, 学生, 一部の社会はプログラムされる.

2022年の振り返りと2023年の抱負

あけましておめでとうございます。 今年も宜しくお願いしますmm

2022年の振り返り

1年がビックリするくらい早く終る、その事に慣れてきました。

  • 筋トレ始める
  • 手首の手術(4/21)
  • サーフボード2本目Quong 7'6"購入(5/11)
  • ベース再開
  • ギター再開
  • SICP再開(9/22)
  • ヨガ始める(11/18)
  • サーフィン152日

4/21に2度目の手首の手術. ようやく全部終わった...

25歳前後の時に挫折した計算機プログラムの構造と解釈に9/22から再度挑戦. 毎日コツコツやっていて、今さっき3章が終わりました(p212/p366(57.92%))。

中学の頃からずっと挫折し続けていた楽器がついに挫折せずに継続出来ています。 Rocksmithさんに感謝.

2023年の抱負

  • SICPを終わらせる
  • サーフィン年間200日を目指す
  • 腹筋割る(あともうちょっと...)
  • ルーパーでフィンガードラム、ベース、ギターで遊べるようになる

SICP 3.5 ストリーム

結構最後の方は問題飛ばしてしまった。 若干飽きてきてしまい...

2周目で深堀りしたいな。

gaucheSICPのdelay

gaucheで遅延評価しようとすると、実装の違いからSICPの意図とはズレていしまいます。 よってpackages/配下に以下のファイルを置いて対応しました。

https://github.com/iori/sicp/tree/main/packages

  • delay-memo.scm
    • メモ化されているdelay
  • delay.scm
    • メモ化されていないdelay
  • stream.scm
    • forceなどdelay,delay-memoと一緒に再読み込みしたい関数たち

github.com

元々参考にしたのはこちらです。

(add-load-path "./packages/" :relative)
(load "prime.scm")
(load "delay-memo.scm")

(print (stream-car (stream-cdr (stream-filter prime? (stream-enumerate-interval 10000 1000000)))))

; ex-3.50
; p.60のmap
; (define (map proc items)
;   (if (null? items)
;     ()
;     (cons (proc (car items))
;           (map proc (cdr items)))))
(define (stream-map proc . argstreams)
  (if (stream-null? (car argstreams))
    the-empty-stream
    (cons-stream
      (apply proc (map stream-car argstreams))
      (apply stream-map
             (cons proc (map stream-cdr argstreams))))))

; ex-3.51
(print "***** ex-3.51")
(define (show x)
  (display-line x)
  x)

(define x (stream-map show (stream-enumerate-interval 0 10)))
; gosh$ (define x (stream-map show (stream-enumerate-interval 0 10)))
; 0
; x

; (stream-ref x 5)
; gosh$  (stream-ref x 5)
;
; 1
; 2
; 3
; 4
; 55

; (stream-ref x 7)
; gosh$ (stream-ref x 7)
;
; 6
; 77

; ex-3.52
(print "***** ex-3.52")
(print "********** memo")

(define sum 0)
(define (accum x)
  (set! sum (+ x sum))
  sum)

(print "sum " sum)
; 0

(define seq (stream-map accum (stream-enumerate-interval 1 20)))
(print "sum " sum)
; 1

; seqの偶数だけ
(define y (stream-filter even? seq))
(print "sum " sum)
; 6

; seqの5で割り切れる数だけ
(define z (stream-filter (lambda (x) (= (remainder x 5) 0))
                         seq))
(print "sum " sum)
; 10

; yの8番目(0start)
(print "(stream-ref y 7): " (stream-ref y 7))
(print "sum " sum)
; 136

(display-stream z)
(newline)
(print "sum " sum)
; 210

(print "********** no memo")
(load "delay.scm")

(define sum2 0)
(define (accum2 x)
  (set! sum2 (+ x sum2))
  sum2)
(print "sum2: " sum2)
; 0

(define seq2 (stream-map accum2 (stream-enumerate-interval 1 20)))
(print "sum2: " sum2)
; 1

(define y2 (stream-filter even? seq2))
(print "sum2: " sum2)
; 6

(define z2 (stream-filter (lambda (x) (= (remainder x 5) 0))
                         seq2))
(print "sum2: " sum2)
; 15

(stream-ref y2 7)
(print "sum2: " sum2)
; 162

(display-stream z2)
(newline)
(print "sum2: " sum2)
; 362

(load "delay-memo.scm")
; ex-3.53
; 2,4,8,16,32

; ex-3.54
(define (add-streams s1 s2)
  (stream-map + s1 s2))
(define (mul-streams f1 f2)
  (stream-map * f1 f2))

(define ones (cons-stream 1 ones))
(define integers (cons-stream 1 (add-streams ones integers)))

(define factorials (cons-stream 1 (mul-streams factorials (add-streams ones integers))))

; gosh$ (stream-ref factorials 0)
; 1
; gosh$ (stream-ref factorials 1)
; 2
; gosh$ (stream-ref factorials 2)
; 6
; gosh$ (stream-ref factorials 3)
; 24
; gosh$ (stream-ref factorials 4)
; 120

; ex-3.55
(define (partial-sums s)
  (cons-stream (stream-car s)
               (add-streams
                 (stream-cdr s)
                 (partial-sums s))))

; gosh$ (stream-ref (partial-sums integers) 0)
; 1
; gosh$ (stream-ref (partial-sums integers) 1)
; 3
; gosh$ (stream-ref (partial-sums integers) 2)
; 6
; gosh$ (stream-ref (partial-sums integers) 3)
; 10
; gosh$ (stream-ref (partial-sums integers) 4)
; 15

; ex-3.56
(define (merge s1 s2)
  (cond ((stream-null? s1) s2)
        ((stream-null? s2) s1)
        (else
          (let ((s1car (stream-car s1))
                (s2car (stream-car s2)))
            (cond ((< s1car s2car)
                   (cons-stream s1car (merge (stream-cdr s1) s2)))
                  ((> s1car s2car)
                   (cons-stream s2car (merge s1 (stream-cdr s2))))
                  (else
                    (cons-stream s1car
                                 (merge (stream-cdr s1)
                                        (stream-cdr s2)))))))))

(define S (cons-stream 1 (merge (scale-stream S 2)
                                (merge (scale-stream S 3)
                                       (scale-stream S 5)))))

; gosh$ (stream-head S 10)
; 1
; 2
; 3
; 4
; 5
; 6
; 8
; 9
; 10
; 12
; done

; ex-3.57
; memo: n-1
; メモ化されていないと計算を毎回行うので指数的に増えていく

; ex-3.58
(define (expand num den radix)
  (cons-stream
    (quotient (* num radix) den)
    (expand (remainder (* num radix) den) den radix)))

; gosh$ (stream-head (expand 1 7 10) 10)
; 1
; 4
; 2
; 8
; 5
; 7
; 1
; 4
; 2
; 8
; done
; gosh$ (stream-head (expand 3 8 10) 10)
; 3
; 7
; 5
; 0
; 0
; 0
; 0
; 0
; 0
; 0
; done

; ex-3.59 ~ 3.62
; skip

; ex-3.63
; (sqrt-stream x)が毎回実行されるので遅い.
; memo化されてなければ代わりはない.

; ex-3.64
(define (average x y)
  (/ (+ x y) 2))

(define (sqrt-improve guess x)
  (average guess (/ x guess)))

(define (sqrt-stream x)
  (define guesses
    (cons-stream 1.0
                 (stream-map (lambda (guess)
                               (sqrt-improve guess x))
                             guesses)))
  guesses)

(define (stream-limit st tolerance)
  (let ((s1 (stream-car st))
        (s2 (stream-car (stream-cdr st))))
    (if (< (abs (- s1 s2)) tolerance)
      s2
      (stream-limit (stream-cdr st) tolerance))))

(define (sqrt x tolerance)
  (stream-limit (sqrt-stream x) tolerance))

; gosh$ (sqrt 2 0.01)
; 1.4142156862745097

; ex-3.64
; skip

SICP 3.4 並列性: 時が本質的

github.com

; ex-3.38
;   Peter: (set! balance (+ balance 10))
;   Paul: (set! balance (- balance 20))
;   Mary: (set! balance (- balance (/ balance 2)))
;
;   a.
;     Peter->Paul->Mary, 110->90->45
;     Peter->Mary->Paul, 110->55->35
;     Paul->Peter->Mary, 80->90->45
;     Paul->Mary->Peter, 80->40->50
;     Mary->Peter->Paul, 50->30->40
;     Mary->Paul->Peter, 50->60->40
;
;  b.
;    Peter->$100にアクセス->新しい値100-10=90->残高を90ドルに設定
;
;    Paul --->$100にアクセス->新しい値100-20=80->残高を80ドルに設定
;
;    銀行:$100->$90->$50->$80
;
;    Mary -->$100にアクセス->新しい値100-50=50->残高を50ドルに設定
;
;    それぞれのbalanceが別で定義されているので、最初に全員が$100にアクセスしてそれを元に計算するからおかしくなる。
;    アクセスのタイミングと新しい値の設定のタイミングによってはもっと複雑になり得る。

; ex-3.39
; 101, 121, 100

; ex-3.40
; 最初のlambdaをP1, 次をP2と呼称する.
;
; 混じらない場合
; P1: 10 * 10 = 100,       P2: 100 * 100 * 100 =   1000000
; P1: 10 * 10 * 10 = 1000, P2: 1000 * 1000 =       1000000
;
; P1が無視された場合
; P2: 10 * 10 * 10 = 1000
;
; P1が混じった場合
; P2: 10 * 100 * 100 = 100000
; P2: 10 * 10 * 100 =  10000
;
; P2が無視された場合
; P1: 100 * 100 = 10000
; P1: 10 * 100 =  1000
; P1: 10 * 10 =   100
;
; 残るのは以下の2つだけ
; P1: 10 * 10 = 100,       P2: 100 * 100 * 100 =   1000000
; P1: 10 * 10 * 10 = 1000, P2: 1000 * 1000 =       1000000

; ex-3.41
; withdrawやdepositなどの書き換えではなくbalanceを返しているだけなので不要.

; ex-3.42
 ;安全な変更, 並列性に違いはない.

; ex-3.43
; 残高の合計の保存が破れる場合
; Paul が withdraw、 deposit 実行時に、 a の値を調べるプロセスと a に新値を Set するプロセスとの間に Peter が a の値を新値に Set してしまう。

; ex-3.44
; 交換(exchange)の場合とは違い残高の差(difference)を計算する必要がなく、transfer 実行時に指定するために Ben Bitdiddle の主張の方が正しい。

; ex-3.45
; withdraw、deposit 手続きの部分で serializer が入れ子になるので処理が終わらない。

; ex-3.46
; cell が true の間は他のプロセスは 相互排除器(mutex)を使えない。
; 2つのプロセスが同時に相互排除器を獲得するとプロセスの相互排除機能が破綻する。

SICP 3.3.5 制約の拡散

github.com

3.34~3.37はスキップします。 これはやらなくても別に困らないというか、もういいかなって。

(define (for-each-except exception procedure list)
  (define (loop items)
    (cond ((null? items) 'done)
          ((eq? (car items) exception) (loop (cdr items)))
          (else (procedure (car items))
                (loop (cdr items)))))
  (loop list))

(define (has-value? connector)
    (connector 'has-value?))

(define (get-value connector)
    (connector 'value))

(define (set-value! connector new-value informant)
    ((connector 'set-value!) new-value informant))

(define (forget-value! connector retractor)
    ((connector 'forget) retractor))

(define (connect connector new-constraint)
    ((connector 'connect) new-constraint))

(define (make-connector)
  (let ((value #f) (informant #f) (constraints '()))
    (define (set-my-value newval setter)
      (cond ((not (has-value? me))
             (set! value newval)
             (set! informant setter)
             (for-each-except setter
                              inform-about-value
                              constraints))
            ((not (= value newval))
             (error "Contradiction" (list value newval)))
            (else 'ignored)))
    (define (forget-my-value retractor)
      (if (eq? retractor informant)
        (begin (set! informant #f)
               (for-each-except retractor
                                inform-about-no-value
                                constraints))
        'ignored))
    (define (connect new-constraint)
      (if (not (memq new-constraint constraints))
        (set! constraints
          (cons new-constraint constraints)))
      (if (has-value? me)
        (inform-about-value new-constraint))
      'done)
    (define (me request)
      (cond ((eq? request 'has-value?)
             (if informant #t #f))
            ((eq? request 'value) value)
            ((eq? request 'set-value!) set-my-value)
            ((eq? request 'forget) forget-my-value)
            ((eq? request 'connect) connect)
            (else (error "Unknown operation - - CONNECTOR"
                         request))))
    me))

(define (adder a1 a2 sum)
  (define (process-new-value)
    (cond ((and (has-value? a1) (has-value? a2))
           (set-value! sum
                       (+ (get-value a1) (get-value a2))
                       me))
          ((and (has-value? a1) (has-value? sum))
           (set-value! a2
                       (- (get-value sum) (get-value a1))
                       me))
          ((and (has-value? a2) (has-value? sum))
           (set-value! a1
                       (- (get-value sum) (get-value a2))
                       me))))
  (define (process-forget-value)
    (forget-value! sum me)
    (forget-value! a1 me)
    (forget-value! a2 me)
    (process-new-value))
  (define (me request)
    (cond ((eq? request 'I-have-a-value)
           (process-new-value))
          ((eq? request 'I-lost-my-value)
           (process-forget-value))
          (else
            (error "Unknown request - - ADDER" request))))
  (connect a1 me)
  (connect a2 me)
  (connect sum me)
  me)

(define (multiplier m1 m2 product)
  (define (process-new-value)
    (cond ((or (and (has-value? m1) (= (get-value m1) 0))
               (and (has-value? m2) (= (get-value m2) 0)))
           (set-value! product 0 me))
          ((and (has-value? m1) (has-value? m2))
           (set-value! product
                       (* (get-value m1) (get-value m2))
                       me))
          ((and (has-value? product) (has-value? m1))
           (set-value! m2
                       (/ (get-value product) (get-value m1))
                       me))
          ((and (has-value? product) (has-value? m2))
           (set-value! m1
                       (/ (get-value product) (get-value m2))
                       me))))
  (define (process-forget-value)
    (forget-value! product me)
    (forget-value! m1 me)
    (forget-value! m2 me)
    (process-new-value))
  (define (me request)
    (cond ((eq? request 'I-have-a-value)
           (process-new-value))
          ((eq? request 'I-lost-my-value)
           (process-forget-value))
          (else
            (error "Unknown request - - MULTIPLIER" request))))
  (connect m1 me)
  (connect m2 me)
  (connect product me)
  me)

(define (constant value connector)
  (define (me request)
    (error "Unknown request - - CONSTANT" request))
  (connect connector me)
  (set-value! connector value me)
  me)

(define (celsius-fahrenheit-converter c f)
  (let ((u (make-connector))
        (v (make-connector))
        (w (make-connector))
        (x (make-connector))
        (y (make-connector)))
    (multiplier c w u)
    (multiplier v x u)
    (adder v y f)
    (constant 9 w)
    (constant 5 x)
    (constant 32 y)
    'ok))

(define (inform-about-value constraint)
  (constraint 'I-have-a-value))

(define (inform-about-no-value constraint)
  (constraint 'I-lost-my-value))

(define (probe name connector)
  (define (print-probe value)
    (newline)
    (display "Probe: ")
    (display name)
    (display " = ")
    (display value))
  (define (process-new-value)
    (print-probe (get-value connector)))
  (define (process-forget-value)
    (print-probe "?"))
  (define (me request)
    (cond ((eq? request 'I-have-a-value)
           (process-new-value))
          ((eq? request 'I-lost-my-value)
           (process-forget-value))
          (else
            (error "Unknown request - - PROBE" request))))
  (connect connector me)
  me)

(define C (make-connector))
(define F (make-connector))
(celsius-fahrenheit-converter C F)

(probe "Celsius temp" C)
(probe "Fahrenheit temp" F)

(set-value! C 25 'user)
(set-value! F 212 'user)
(forget-value! C 'user)
(set-value! F 212 'user)

; ex-3.33
(define (averager a b c)
  (let ((u (make-connector))
        (x (make-connector)))
    (adder a b u)
    (multiplier c x u)
    (constant 2 x)
    'ok))

(define A (make-connector))
(define B (make-connector))
(define C (make-connector))
(averager A B C)
(probe "A" A)
(probe "B" B)
(probe "C" C)

SICP 3.3.4 ディジタル回路のシミュレーター

う〜ん、今回もまた「最後に完成するまで動かせない」系の奴でちょっとテンション下がってしまった。

  • 入力したいデータ、出力で欲しいデータはわかっている
  • 大枠を作っていく
  • 細かいところは後で作っていく

これは良いんだけど、細かい部分が未実装だから動かせないのに大枠の部分を改良せよ!っていう問題が多くて...実際に動かしながらTry&Errorで確認しつつ考えるってことが出来なくて辛い。apply-genericもそうだったけど...

ただ今日はSICPのこのやり方って

  • 欲しい結果がわかっているから先にUTを書く
  • mock,stubを導入して未実装の細かい部分を動かせるようにする

をしたらTDDになるんだな〜と思いました。

まだ先は長いし読み終わってすぐに2周目やらないけど、2周目する時はその辺を意識してgaucheのUTとか調べてやってみるといいかも。

SICP3章終わったら息抜きに違う軽めの技術書一冊やるかなぁ。ほぼ毎日やって70日目でちょっと飽きてきた感は出てきた。飽きてきたというか、他のこともしたいなーと。いやそれが飽きか(笑)

  • 71日目
  • p166->p168/p366(45.9%)

github.com

; 3.3.4 ディジタル回路のシミュレーター

; インバーター: 入力を反転する(0->1, 1->0)
; アンドゲート: 2つの入力が共に1なら1. 1つでも0なら0.
; オアゲート:   2つの入力の内、1つでも1なら1. 両方0なら0.
; それぞれは遅延する

; ex-3.28
(define (logical-or s1 s2)
  (cond ((or (= s1 1) (= s2 1)) 1)
        ((and (= s1 0) (= s2 0)) 0)
        (else (error "Invalid signal" s1 s2))))

(define (or-gate a1 a2 output)
  (define (or-action-procedure)
    (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-procedure)
  (add-action! a2 or-action-procedure)
  'ok)

; ex-3.29
; A-|I|-C--|
;         |AND|-E-|I|-out
; B-|I|-D--|
;   1+inverter-delay
;          1+and-gate-delay
;                  1+inverter-delay
; 遅延時間はinverter-delay + inverter-delay + and-gate-delay
(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)
    'ok))

; ex-3.30
(define (ripple-carry-adder list-a list-b list-s c)
  (define (iter list-a list-b list-sum c-in)
    (if (not (null? list-a))
      (let ((c-out (make-wire)))
        (full-adder (car list-a) (car list-b) c-in (car list-sum) c-out)
        (iter (cdr list-a) (cdr list-b) (cdr list-sum) c-out))
      'ok))
  (iter list-a list-b list-sum c-out))

; ex-3.31
; after-delayを実行してthe-agendaに手続きを追加したいから。
; after-delayが実行されないのでthe-agendaに手続きが追加されず、propagateが回らない.

; ex-3.32
; 次第書きのアクションが最後に入ったものが最初に出る(LIFO)の場合は、”回線bに登録されたアクション”、”回線aに登録されたアクション” の順で実行される。
; この場合は出力回線 c に変化は起こらず 0 のままとなりシミュレーションが正常に実行されない。