SICP 4.4.4.5 Ex. 4.74

Ex. 4.74

a.

negate の例を考える。

(and (supervisor ?x (Warbucks Oliver)) (not (same ?x (Scrooge Eben))))

最初の (supervisor ?x (Warbucks Oliver)) で作られるフレームストリームは

[ {(?x . (Aull DeWitt))} {(?x . (Scrooge Eben))} {(?x . (Bitdiddle Ben))} ]

である。( )は変数束縛、{ } はフレーム、[ ] はフレームストリームとする。

次の (not ...) は negate 関数を呼び出す。

(negate '((same ?x (Scrooge Eben)))
        '[ {(?x . (Aull DeWitt))} {(?x . (Scrooge Eben))} {(?x . (Bitdiddle Ben))} ] ) 

negateの最初の引数には (not (same...))の cdr が渡されるので、((same ...)) とカッコが2重になっている。

negate の定義に simple-stream-flatmap を使った場合の動作

(negate '((same ?x (Scrooge Eben))) 
        '[ {(?x . (Aull DeWitt))} {(?x . (Scrooge Eben))} {(?x . (Bitdiddle Ben))} ] )
  ↓
(simple-stream-flatmap
 (lambda (frame)
   (if (stream-null? (qeval (negated-query '((same ?x (Scrooge Eben))))
                            (singleton-stream frame)))
       (singleton-stream frame)
       the-empty-stream))
 '[ {(?x . (Aull DeWitt))} {(?x . (Scrooge Eben))} {(?x . (Bitdiddle Ben))} ] ))
  ↓
(simple-flatten
 (stream-map 
   (lambda (frame)
     (if (stream-null? (qeval '(same ?x (Scrooge Eben))
                              (singleton-stream frame)))
         (singleton-stream frame)
         the-empty-stream))
 '[ {(?x . (Aull DeWitt))} {(?x . (Scrooge Eben))} {(?x . (Bitdiddle Ben))} ] )))
  ↓
map の適用。
 1個目のフレーム {(?x . (Aull DeWitt))} の結果は [ {(?x . (Aull DeWitt))} ]
 2個目のフレーム {(?x . (Scrooge Eben))} の結果は the-empty-stream
 3個目のフレーム {(?x . (Bitdiddle Ben))} の結果は [ {(?x . (Bitdiddle Ben))} ]
結局 map の結果は 
  [ [{(?x . (Aull DeWitt))}] the-empty-stream [{(?x . (Bitdiddle Ben))}] ] 
というストリームのストリームになる。
negateはストリームを増やすことはないので、
ストリームの中のストリームはシングルトンストリームまたは the-empty-stream のみである。
  ↓
(simple-flatten 
  `[ [{(?x . (Aull DeWitt))}] ,the-empty-stream [{(?x . (Bitdiddle Ben))}] ] )
  ↓
(stream-map <??>①
  (stream-filter <??>② 
                 `[ [{(?x . (Aull DeWitt))}] ,the-empty-stream [{(?x . (Bitdiddle Ben))}] ] ))

となる。( `はquasiquote , はunquote。実験するときは注意。)

ここまでくれば、<??>② は the-empty-stream を取り除くフィルターであり、<??>①はストリームの中のシングルトンストリームをフレームに下げる関数。要するに car であると分かる。

ということで答えは

(define (simple-flatten stream)
  (stream-map (lamda (x) (car x))
      	       (stream-filter (lambda (x) (not (eq? x the-empty-stream))) stream)))

b.

この問題の negate の場合

(negate '((same ?x (Scrooge Eben))) 
        '[ {(?x . (Aull DeWitt))} {(?x . (Scrooge Eben))} {(?x . (Bitdiddle Ben))} ] )
 ↓
[ {(?x . (Aull DeWitt))} . (delay {(?x . (Bitdiddle Ben))}) ] 

既存の negate の場合

(negate '((same ?x (Scrooge Eben))) 
        '[ {(?x . (Aull DeWitt))} {(?x . (Scrooge Eben))} {(?x . (Bitdiddle Ben))} ] )
 ↓
[ {(?x . (Aull DeWitt))} . (delay {(?x . (Bitdiddle Ben))}) ] 

となる。(flatten-streamは一見 the-empty-stream をフィルタしてないように見えるが、下位関数のinterleave-delyed が the-empty-stream を読み飛ばしている。)

ということで、結果のストリームはどちらも同じである。(当たり前か。結果変わってたら提案以前のバグだ。)

delayのタイミングについても、この問題の negate は入力ストリームの2個目以降の処理を遅延する。既存の negate も同じである。

ということで、他になんか?何もなければ、negate については、特に振る舞いは変わらないような気がする。

lisp-value, find-assertionsについては、、、疲れたので略。