SICP 4.4.4.5 Ex. 4.70

  • 投稿日:
  • カテゴリ:

Ex. 4.70

まず、THE-ASSERTIONSの中身は

THE-ASSERTIONS =
((son Adam Cain) . (delay ((son Cain Enoch) . (delay ((son Enoch Irad) ... )))

これの格納の様子を図にするとこんな感じ。 THE-ASSERTIONS

図のように、Schemeはリストを変数に代入するとき、リストのコピーはとらない。ポインタだけ格納する。promiseは delay で作られるオブジェクトである。(残念ながら、promiseオブジェクトの内部のデータの持ち方は不明。多分これもオブジェクトへのポインタを持つだけと思う。)

局所変数 old-assertion を使わずに set! するとどうなるか見てみよう。

(set! THE-ASSERTIONS (cons-stream '(son abc xyz) THE-ASSERTION))

THE-ASSERTIONS 2

ストリームの中のTHE-ASSERTIONSも新しい番地を指すだけである。前回のTHE-ASSERTIONSの番地を誰も覚えていない。この場合

(car THE-ASSERTIONS) → (son abc xyz)
(car (force (cdr THE-ASSERTIONS))) → (son abc xyz)
...

と、いくら cdr を繰り返しても、(son abc xyz) しか返ってこない。

局所変数 old-assertions を使った場合は

(let ((old-assetions THE-ASSERTIONS))
     (set! THE-ASSERTIONS (cons-stream '(son abc xyz) old-assertions)))

THE-ASSERTIONS 3

こんな感じになる。前回のTHE-ASSERTIONSの番地をold-assertions が覚えている。これだと

(car THE-ASSERTIONS) → (son abc xyz)
(car (force (cdr THE-ASSERTIONS))) → (son Adam Cain)
...

と、cdr で次々に表明を取り出せる。

ある言語の変数の保存方法は、こうでなければならないというロジカルな理由はない。若しくは、どんな格納の仕方でもロジカルに正しいと言える。CとかFORTRANの代入と違う動作だといって文句は言えない。

Scheme で cons-stream と set! の組み合わせが上の図のように変数を格納するのは、単に仕様である。

しかし、一見なんの意味もない、省略可能な中間変数にしか見えない old-assertions が実は必須だった。というのは罠以外の何者でもない。

この問題の意図は、cons-stream 使ったグローバル変数の set! には、こういう落とし穴があるので注意しましょう。ということである。

念のため、もし delay を使わないで、cons だけで THE-ASSERTIONS を作ったらどうなるであろうか?

この場合は、局所変数 old-assertions は要らない。CとかFORTRANの代入と同じように振る舞う。

(set! THE-ASSERTIONS (cons '(son abc xyz) THE-ASSERTION))

THE-ASSERTIONS 4

ということで、cons と cons-stream で set! の振る舞いが異なる。非常に間違い易い、、、だが、仕様である!我々は黙って注意するしかない。

(そもそも、THE-ASSERTIONSで cons-stream を使うメリットがないような気がする。consで十分ではないか?)