SICPを読む(3)

飛ばしていた練習問題の消化

○ 問題 1.4

C++にずっぷりと使った身としては、かなり奇妙な感じに思える構文。

a + abs(b) を行うような関数だけれども、

  • 評価した結果として演算子自身を返すことができる
  • 評価の値を、入れ子状にして使うことができる

というのが、なじめなくて一見した感じでは「む?」と考えてしまう。

とりあえずは、こんなサンプルを打って動作を確認

(define (a-plush-abs-b a b)
  ((if (> b 0) + -) a b))

(print (a-plush-abs-b 10 5))
(print (a-plush-abs-b 10 -5))

(結果)

15
15

振る舞いを考えてみると"(if (> b 0) + -)"の部分が、bが0より大きい場合には、+(プラス)を
返し、小さい場合には-(マイナス)を返す。
よって、bが0より大きい場合には"(+ a b)"となり、bが負の数の場合"(- a b)"となる。
よって、"a + abs(b)"として用いることができる。

○ 問題 1.5

まずはまとめをしてみる。

作用的順序による評価は、引数として渡すときに評価を行う方法で、正規順序の評価は、基本的演算子
でてくるまでは式の置き換えを繰り返しす。

すなわち、

(define (sum a b) (+ a  b))

という関数があったとして、

(sum (+ 10 20) (- 20 10))
||M

として評価した場合。

作用的順序による評価では、

>||
1. (sum (+ 10 20) (- 20 10))
2. (sum 30 10)
3. (+ 30 10)
4. 40

といった手順で評価され、正規順序による評価では

1. (sum (+ 10 20) (- 20 10))
2. (+ (+ 10 20) (- 20 10))
3. (+ 30 10)
4. 0

といった手順で評価される。
さて、問題の

(define (p) (p))

(define (test x y)
  (if (= x 0)
      0
      y))

(test 0 (p))

を考えてみる。

正規順序による評価では

1. (test 0 (p))
2. (if (= x 0) 0 (p))
3. 0

となる。
注目するべき点は、2.の"(= x 0)"から"(p)"を評価する必要が無いという点にある。

作用的順序では、

1. (test 0 (p))
2. (test 0 (p))
3. (test 0 (p))
...

となり、"(define (p) (p))"の定義により、再起的に"(p)"が評価され続け、結果にたどり着くことが
できない。

ちなみに、同じプログラムをGaucheで実行してみると、当然応答しなかった