Reasoned Schemer (111) bumpo、gen&test+o、enumerate+o、enumerateo
bumpo
(defrel (bumpo n x)
(conde ((== n x))
((fresh (m) (-o n '(1) m) (bumpo m x)))))
これは問題ない
gen&test+o
(defrel (gen&test+o i j k)
(onceo (fresh (x y z)
(+o x y z)
(== i x) (== j y) (== k z))))
これの意味が分からない
足してz
になるx
とy
をいっぱい作ってから
i
がx
と同じでj
がy
と同じでk
がz
と同じかどうか試している
最初っからi
とj
を足したらk
になるかどうか試せばいいだけじゃない?
(defrel (mygen&test+o i j k) (onceo (+o i j k)))
って定義すれば
> (run* q (gen&test+o '(0 0 1) '(1 1) '(1 1 1)))
'(_0)
> (run* q (test+o '(0 0 1) '(1 1) '(1 1 1)))
'(_0)
ほら同じ、ってこれだけじゃわかんないけど
> (run 1 q (gen&test+o '(0 0 1) '(1 1) '(0 1 1)))
(no value)
> (run 1 q (mygen&test+o '(0 0 1) '(1 1) '(0 1 1)))
'()
むしろ優秀じゃね?
それとも試し続ける必要がある?
enumerate+o
(defrel (enumerate+o r n)
(fresh (i j k)
(bumpo n i)
(bumpo n j)
(+o i j k)
(gen&test+o i j k)
(== `(,i ,j ,k) r)))
i
を列挙してj
を列挙してk
をi+j
にする
これで終わりでいいんじゃないの(r
は設定するとして
(gen&test+o i j k)
は何してるの
(gen&test+o i j k)
抜きで定義して・・・
(defrel (myenumerate+o r n)
(fresh (i j k)
(bumpo n i)
(bumpo n j)
(+o i j k)
(== `(,i ,j ,k) r)))
実行してやると
> (run* s (myenumerate+o s '(1 1)))
'(((1 1) (1 1) (0 1 1))
((1 1) (0 1) (1 0 1))
...
((1) (1) (0 1))
((1) () (1)))
答えの出てくる順番は違うけど別にこれでもよくない?
狙いがわからない
Do we need
gen&test+o
(gen&test+o
って必要?)
Not at all (いやまったく)
あ、そうだよねそういう流れだよね
(snip) so we can replace
(gen&test+o i j k)
with theonceo
expression unchanged inenumerate+o
.
だから、enumerate+o
の(gen&test+o i j k)
はonceo
の式で置き換えられますよ
え?
(gen&test+o i j k)
は削っても大丈夫って話じゃないの?
なんなのこれ
(defrel (enumerate+o r n)
(fresh (i j k)
(bumpo n i)
(bumpo n j)
(+o i j k)
(onceo (fresh (x y z)
(+o x y z)
(== i x) (== j y) (== k z)))
(== `(,i ,j ,k) r)))
最後のgoal
(defrel (enumerateo op r n)
(fresh (i j k)
(bumpo n i)
(bumpo n j)
(op i j k)
(onceo (fresh (x y z)
(op x y z)
(== i x) (== j y) (== k z)))
(== `(,i ,j ,k) r)))
上のgoalはただ+o
をop
に置き換えやすいようにしただけなのかな
(gen&testo op i j k)
みたいなの定義してもよさそうだけど
高階goal?goalを引数として渡してるのは新鮮
> (run* s (enumerateo +o s '(1 1)))
'((() (1 1) (1 1))
((1 1) () (1 1))
...
((1 1) (1 1) (0 1 1))
((1 1) (0 1) (1 0 1)))
> (run* s (enumerateo *o s '(1 1)))
'(((0 1) (1 1) (0 1 1))
((1) (1 1) (1 1))
...
(() () ())
((1 1) (1 1) (1 0 0 1)))
そもそもop
が2回出てくる意味がわからないわけだが
ここから応用範囲が広がりそうな気はする
(でもReasoned Schemerではここまで)
/o
とかlogo
に使えるようにしたバージョン
(defrel (enumerate2o op r n)
(fresh (i j k h)
(bumpo n i)
(bumpo n j)
(op i j k h)
(onceo (fresh (x y z w)
(op x y z w)
(== i x) (== j y) (== k z)))
(== `(,i ,j ,k ,h) r)))
文字を増やしただけ
> (run* s (enumerate2o /o s '(1 1)))
'(((1) (1 1) () (1))
(() (1 1) () ())
...
((1 1) (1) (1 1) ())
((0 1) (1) (0 1) ()))
> (run* s (enumerate2o logo s '(1 1)))
'(((1 1) (1 1) (1) ())
((1 1) () (_0 . _1) (1 1))
...
((0 1) (1) (_0 . _1) (1))
((0 1) (1 1) () (1)))
10章は一度読み返してるし、これで終わりでいいかな
やっぱり関係プログラミング脳になった感じはしない
大変だなあという感想
わざわざ大変なところをやったのかもしれない
Prolog勉強したら印象は違ってくるのかな
でも今はやらない