問題4.71-4.75

久しぶりに。Schemeはやっぱり楽しい。続き。

4.71

Louisの手続きでdelayが取り除かれた箇所はapply-rulesとdisjoinの再帰部分ですね。とりあえずわかんないのでdelayedでないstream-appendとinterleaveを書いてみます。

(define (stream-append s1 s2)
  (if (stream-null? s1)
      s2
      (cons-stream
       (stream-car s1)
       (stream-append (stream-cdr s1) s2))))

(define (interleave s1 s2)
  (if (stream-null? s1)
      s2
      (cons-stream
       (stream-car s1)
       (interleave s2 (stream-cdr s1)))))

ついでにLouisのも書いてみます。

(define (simple-query query-pattern frame-stream)
  (stream-flatmap
   (lambda (frame)
     (stream-append (find-assertions query-pattern frame)
                    (apply-rules query-pattern frame)))
     frame-stream))

(define (disjoin disjuncts frame-stream)
  (if (empty-disjunction? disjuncts)
      the-empty-stream
      (interleave
       (qeval (first-disjunct disjuncts) frame-stream)
       (disjoin (rest-disjuncts disjuncts) frame-stream))))

うーん考えてもわかんない。こういうときはググります。...なるほど。
disjoinを考えてみます。再帰だからinterleaveの入れ子みたいな感じになるはず。qevalは拡張されたフレームのストリームを生成するから、それがinterleaveの引数に渡される。そうすると、disjoinは質問言語のorを処理する関数で、disjunctsはorの各引数なので、

(or <exp1> <exp2> ... <expn> ...)

のような場合について、まずの探索をして拡張されたストリームを生成し、そしての探索をして拡張されたストリームを生成し、...、の探索をして拡張されたストリームを生成し、...、...生成して、そしてinterleaveに渡すことができます。これはorの全部を探索するならいいんだけど、全部じゃなくて式を満たすいくつかの場合を早くしりたい場合、冗長です。simple-queryもapply-rulesが全部終わらないとstream-appendしてくれないので冗長。或いは順に評価していくとマッチするパターンがあるけど、その後で無限ループになってしまうような式がある場合、値を一つも印字せずに終わってしまったりすることがあるんじゃないでしょうか。そういえば

;;; Query input:
(reverse (a b c) ?x)
;;; Query results:
(reverse (a b c) (c b a))

;;; Query input:
(reverse ?x (a b c))
;;; Query results:
. user break

こんなものがありました。なのでこれとorを使ってとてもわざとらしい例を作ってみたいと思います。まずはdelayedを使った評価器。

;;; Query input:
(or (son Adam ?x) (reverse ?y (a b c)))
;;; Query results:
(or (son Adam Cain) (reverse ?y (a b c)))
. user break

ちゃんと値を一つは生成してくれます。次にLouisの手続きを使った評価器。

;;; Query input:
(or (son Adam ?x) (reverse ?y (a b c)))
;;; Query results:
. user break

一つも生成してくれません。ということでdelayを使った方が最悪無限ループに陥ってもいくつか有用な値を得られるという点で有効のようです。

4.72

interleaveは二つのストリームs1とs2を混ぜ合わせるものでした。なんぜこんなことしなきゃいけないのかですが、単にstream-appendした場合、s1が無限ストリームだったりしたら永遠にs2の出番が廻ってこないからです。なのでinterleave-delayedを使うと、s1が無限、s2が有限の質問をしてもs2でマッチしたものはちゃんと全部出てくるという結果になるはず(s1,s2ともに無限でも偏った順に結果が出てくるということはないということも言えます)。

;;; Query input:
(or (last-pair ?x (3)) (son Ada ?y))
;;; Query results:
. user break

あれ?
あぁ、そうか、単に無限ループを持ってくるんじゃなくて、無限のストリームを生成する質問を持ってこないとだめなのか。うーんそんな質問あるかな。
ありました。p276。

;;; Query input:
(assert! (rule (married ?x ?y) (married ?y ?x)))
Assertion added to data base.

;;; Query input:
(assert! (married Minn** Mick**))
Assertion added to data base.

;;; Query input:
(married Mick** ?who)
;;; Query results:
(married Mick** Minn**)
(married Mick** Minn**)
(married Mick** Minn**)
...

こいつらをそのまま扱うのは怖いのでぼかします。試してみます。

;;; Query input:
(or (married Mick** ?who) (son Ada ?x))
;;; Query results:
(or (married Mick** Minn**) (son Ada ?x))
(or (married Mick** ?who) (son Ada Jubal))
(or (married Mick** Minn**) (son Ada ?x))
(or (married Mick** ?who) (son Ada Jabal))
(or (married Mick** Minn**) (son Ada ?x))
(or (married Mick** Minn**) (son Ada ?x))
...

ということでちゃんとJubalとJabalが出てきてくれました。じゃあdisjoinをstream-append-delayedに変更して試してみます。

;;; Query input:
(or (married Mick** ?who) (son Ada ?x))
;;; Query results:
(or (married Mick** Minn**) (son Ada ?x))
(or (married Mick** Minn**) (son Ada ?x))
(or (married Mick** Minn**) (son Ada ?x))
(or (married Mick** Minn**) (son Ada ?x))
...

出てきません。ということで、interleaveは有効にはたらくようです(でも結構レアケースな気がするけど)。

4.73

streamが無限の時、flatten-streamの再帰が終わらないから。flatten-streamを問題文のように変えてやってみます。

> (define ones (cons-stream 1 ones))
> (define integers (cons-stream 1 (stream-map + ones integers)))
> (disp-stream (flatten-stream integers) 20)
. user break

ということでだめです。

4.74

a.

flatten-streamの動作を見てみます。

> (define ones (cons-stream 1 ones))
> (define twos (cons-stream 2 twos))
> (define integers (cons-stream 1 (stream-map + ones integers)))
> (define 2^n (cons-stream 1 (stream-map * twos 2^n)))
> (define s
    (cons-stream
     (cons-stream ones the-empty-stream)
     (cons-stream
      (cons-stream twos
                   (cons-stream integers
                                the-empty-stream))
      (cons-stream
       (cons-stream 2^n the-empty-stream)
       the-empty-stream))))
> (define fs (flatten-stream s))
> (disp-stream (stream-ref fs 0) 10)
(1 1 1 1 1 1 1 1 1 1)
> (disp-stream (stream-ref fs 1) 10)
(2 2 2 2 2 2 2 2 2 2)
> (disp-stream (stream-ref fs 2) 10)
(1 2 4 8 16 32 64 128 256 512)
> (disp-stream (stream-ref fs 3) 10)
(1 2 3 4 5 6 7 8 9 10)

CLのMAPCANの非破壊的なもののストリームバージョンで、interleaveによる混ぜ合わせが組み込まれてます。Alyssaの要求にしたがってinterleaveを取り除くとこんな感じになるはずです。

(define (simple-stream-flatmap proc s)
  (simple-flatten (stream-map proc s)))
(define (simple-flatten stream)
  (stream-map stream-car
              (stream-filter
               (lambda (s) (not (stream-null? s))) stream)))

空ストリームか単一ストリームのみを生じるのでstream-carでおけーです。実験してみます。

> (define ones (cons-stream 1 ones))
> (define twos (cons-stream 2 twos))
> (define integers (cons-stream 1 (stream-map + ones integers)))
> (define 2^n (cons-stream 1 (stream-map * twos 2^n)))
> (define s
    (cons-stream
     (cons-stream ones the-empty-stream)
     (cons-stream
      (cons-stream twos the-empty-stream)
      (cons-stream
       (cons-stream integers the-empty-stream)
       (cons-stream
        (cons-stream 2^n the-empty-stream)
        the-empty-stream)))))
> (define fs (simple-flatten s))
> (disp-stream (stream-ref fs 0) 10)
(1 1 1 1 1 1 1 1 1 1)
> (disp-stream (stream-ref fs 1) 10)
(2 2 2 2 2 2 2 2 2 2)
> (disp-stream (stream-ref fs 2) 10)
(1 2 3 4 5 6 7 8 9 10)
> (disp-stream (stream-ref fs 3) 10)
(1 2 4 8 16 32 64 128 256 512)

ちゃんと動きます。単一ストリームのみということなのでsは前のと変えてあります。

b.

Alyssaは頭いいからたぶん変わらない。
interleaveを導入するのは無限ストリームとかに対応するためだけど、それが生じない場合なら別にいらない。Alyssaの言い分ではnegate,lisp-value,find-assertionsは空ストリームか単一のストリームしか返さないので、実際そうなら動作は変わらないはず。
コードを見てみるとどれも戻ってくる値はthe-empty-streamかsingleton-streamで生成したもので、singleton-streamは

(define (singleton-stream x)
  (cons-stream x the-empty-stream))

なので単一ストリームを生成するからstream-carで取り出せる。動作は変わらない。

4.75

uniqueを実装。手続きを表に格納しているから評価器に組み込むのは楽でいいですね。
notに類似ということなので、notに倣って書いてみます。

(define (uniquely-query exps) (car exps))
(define (uniquely-asserted contents frame-stream)
  (stream-flatmap
   (lambda (frame)
     (let ((result (qeval (uniquely-query contents)
                          (singleton-stream frame))))
       (if (and (not (stream-null? result))
                (stream-null? (stream-cdr result)))
           result
           the-empty-stream)))
   frame-stream))
(put 'unique 'qeval uniquely-asserted)

試してみます。

;;; Query input:
(unique (son Adam ?x))
;;; Query results:
(unique (son Adam Cain))

;;; Query input:
(unique (son Ada ?x))
;;; Query results:

;;; Query input:
(and (job ?x ?y)
     (unique (supervisor ?z ?x)))
;;; Query results:
(and (job (Scrooge Eben) (accounting chief accountant)) (unique (supervisor (Cratchet Robert) (Scrooge Eben))))
(and (job (Hacker Alyssa P) (computer programmer)) (unique (supervisor (Reasoner Louis) (Hacker Alyssa P))))

どうやら合っているようです。


今日はここまで。次で4章終わりです。