SICP解答メモ兼解説(1.2〜1.5)

1.1

実際に試してみてわかるのでパス

1.2

わかりやすいように分子と分母を分けて考える。まず分子から。

分子

 分子は5、4、(2-(3-(6+4/5)をそれぞれ足した数。
ここで一番ネストの深い所の「6+4/5」からScheme式に表現する。Scheme式に書くと


(+ 6 (/ 4 5)) … 式2
式2を3から引く。

(- 3 (+ 6 (/ 4 5))) … 式3
次に、式3を2から引く。

(- 2 (- 3 (+ 6 (/ 4 5)))) … 式4
そして、式4と5と4を足す。

(+ 5 4 (- 2 (- 3 (+ 6 (/ 4 5))))) … 式5
これで分子は完成。次に分母を組み立てていく。

分母

分母は3、(6ー2)、(2ー7)をかけたものなので


(* 3 (- 6 2) (- 2 7)) … 式6
とするだけ。
あとは式5を式6で割る表現書けばよい。

(/ (+ 5 4 (- 2 (- 3 (+ 6 (/ 4 5))))) (* 3 (- 6 2) (- 2 7)))
これで完成。

1.3

以下のようなアルゴリズムで組み立てたい。

  • x,yどちらが大きいのか?
  • xの方が大きいとき
    • yとzどちらが大きいのか?
    • yの方が大きいとき


(* x y)

    • zの方が大きいとき


(* x z)

  • yの方が大きいとき
    • xとzどちらが大きいのか?
    • xの方が大きいとき


(* y x)

    • zの方が大きいとき


(* y z)
これをSchemeで表現する。名前は適当につけて、

(define (larger-sum-of-squere x y z)
    (cond ((> x y)
        (cond ((> y z) (* x y))
              ((< y z) (* x z))
        ))
          ((< x y)
        (cond ((> x z) (* y x))
              ((< x z) (* y z))
        ))
    )
)

のように書く。なんかインデントが変な気がするが…あと、もっと綺麗な書き方は解答集のように特殊フォームifを使って表現すればいいと思う。あのifの使い方は「ああ、俺Lispでプログラミングしてるよ」って気分になれてよい。それにしても、アルゴリズムを文で書くと見にくいことこの上ないなあ。フローチャートでもかけばいいのだが…。

問題1.4

以下のような文追加を追加して実行してみる。


(print (a-plus-abs-b 2 0))
そうすると、「2」と表示される。第一引数が正なので、

(+ 2 0)
が実行された。今度は

(print (a-plus-abs-b -1 -2))
としてみる。今度は「1」と表示された。これは、第一引数が負なので

(- -1 2)
が実行されたことになる。このような手続きがうまくいくのは、解釈系が作用的順序で評価してるからなのだろうなあ。

解答例@解答集

問題1.5

正規順序(展開しきって評価)の場合
  1. まず、手続き「test」が展開される。
  2. 仮引数が置き換えられる。
  3. (x=0)なので、「0」が返される。
作用的順序(引数→手続きの順で評価)の場合
  1. 第一引数の「0」を評価。
  2. 第二引数の「(p)」を評価。
  3. 手続き「p」は、自分を呼び出し続けるので、無限ループになる。


Gaucheの解釈系は、作用的順序で評価されるので無限ループする。

解答例@解答集