SICP 4.3.1 Ex. 4.35 Ex. 4.36 Ex. 4.37

  • 投稿日:
  • カテゴリ:

amb と require の動作の確認

amb1
...
amb2
  ...
  require
  ...
  1. require が失敗すると、require の前に実行された amb2 に処理が戻る
  2. amb2 はバックトラックして、その位置からプログラムが再実行する
  3. amb2 のリストが終了してて バックトラックできない場合、その前のamb1 に戻る(バックアップ)
  4. amb1 もバックトラックしてその位置からプログラムが再実行する
  5. amb1 のリストが終了していてバックトラックできない場合、前の amb に戻りたいが、ないので先頭まで戻って終了する。
  6. require が成功した場合は、そのまま下の式を実行してプログラムの最後まで行って終了する。
  7. プログラムの最後まで行って終了した場合、評価器の駆動ループが (require false) を発行して処理を前の amb に戻す。

図にするとこんな感じ

なお、require 失敗時の戻り先の amb をどれにするかは、関数の親子関係には全く関係がない。また、実行フレームの親子関係にも全く関係ない。関係あるのは、実行の順番だけである。require より以前に実行された amb に戻ることになっている。例えば

(define (f) 
  ...
  (amb 1 2 3)
  ...)

(define (g) (require false))

(begin ...
       f
       ...
       g)

を実行した場合、g の中の require の失敗時の戻り先は、f の中の amb である。なぜなら、f の中の amb は、require より以前に実行されているからである。

まとめると、基本的な動作は、(amb ...) を実行して、リストが終わっていた場合、その前に実行した amb に戻る(バックアップ)ということである。

require の失敗というのは、(amb) を実行することである。これはambのバックアップ動作を利用して前の amb に戻っているのである。

逆に言えば、無条件に以前の amb に戻りたいときは、(require false) とか (amb) を実行すればよいということである。

Ex. 4.35

p.246 の an-element-of 、an-integer-starting-from と同じようにつくる

(define (an-integer-between s e)
  (require (<= s e))
  (amb s (an-integer-between (+ s 1) e)))

Ex. 4.36

i, j, k が無限ストリームになった場合の探索の様子を見てみる

こんな感じで、探索が k の無限ストリームにつかまって返ってこなくなる。

探索ツリーの途中や末端が無限ストリームになると、探索が返ってこなくなる。なので、 修正方法は、最初のストリームだけ無限にして、途中や末端のストリームは有限にすることである。

(define (a-pythagorean-triple-between low high)
  (let ((k (an-integer-starting-from low)))
    (let ((i (an-integer-between low k)))
      (let ((j (an-integer-between i k)))
        (require (= (+ (* i i) (* j j)) (* k k)))
        (list i j k)))))

i, j は三角形の短辺、k は三角形の長辺を指している。短辺は長辺より長くなることはないので。

Ex. 4.37

2重の amb に対して、2重の require がある場合。

amb1
amb2
  require1
  require2

こんな感じ。この場合、この本の評価器の行う探索は、

  1. require1 によるツリーの枝刈りは行わない。ツリーは全て探索される。
  2. require1、require2 が2つあるからといって、探索を2回行うことはない。探索は1回だけである。

である。まとめると、評価器は amb1、amb2 の作るツリーの全体を1度だけ探索する。ということである。

枝刈りをしないので、効率の良い悪いは、単純にツリーの大小で比べられる。

で、low=1, high=5 のとき、Benさんのプログラムの探索ツリーは、こんな感じになる。

調査の必要な枝は 15本。一方、前問(Ex. 4.35)は amb が3重になっており、探索ツリーはこんな感じになる。

調査の必要な枝は 35本である。Benさんのほうが効率が良い。