Reasoned Schemer (67) 関数整理
ひとつひとつの関数のお仕事をひとことで言えて
やってみよう
その前に用語のまとめ
- association: carが変数で、cdrが変数または値のpair
- cdrの値には変数が含まれることもある
- substitution: associationのリスト
- 同じ変数がcarに2回以上出てくることはない
- stream: substitutionのリスト(suspensionあり)
- goalが成功するための条件みたいなもの
- Anがassociationだとすると
((A1 A2) (A3))
というストリームは「A1かつA2であるかまたはA3である」という条件みたいなもの(まだたぶんうまく言えてない)
- goal: substitutionを初期条件として取り、substitutionのstreamを返す関数
- reified-name substitution: 変数とreified-nameを置き換えるsubstitution
呼び出し関係を軽く意識して並べてみる
とりあえず小物
(var name)
:name
という名前の変数を定義する(var? x)
:x
は変数かempty-s
: からっぽのsubstitution
ユニフィケーション
(unify u v s)
:u
とv
が同じになるようにsubstitusions
を拡張する(walk v s)
: 変数v
をsubstitutions
で置き換える- 置き換えた結果が変数であればさらに置き換えを進める
- リストになったらそれ以上は置き換えない
(ext-s x v s)
: 変数x
と値v
のassociationでsubstitutions
を拡張する- 拡張できない場合は
#f
(occurs? x v x)
: 変数x
と値v
のassociationがsubstitutions
に循環を作るか
- 拡張できない場合は
goal
succeed
: 常に成功するgoalfail
: 常に失敗するgoal(== u v)
:u
とv
をs
でunify
したstreamを返すgoal(disj2 g1 g2)
:g1
とg2
のどちらがが成功すれば成功するgoal(g1 s)
と(g2 s)
をappend-inf
して返す(append-inf s-inf t-inf)
: ふたつのstreams-inf
とt-inf
をひとつのstreamにつなぐ
(conj2 g1 g2)
:g1
とg2
の両方が成功するときだけ成功するgoal- stream:
(g1 s)
の各要素(substitution)にg2
を適用する (append-map-inf g s-inf)
: streams-inf
の各要素にgoalg
を適用する
- stream:
run
(run n (x0 x ...) g ...)
: goalg ...
を満たす変数の組x0 x ...
をn
個求める(call/fresh name f)
:name
という名前の変数を作ってf
に適用するf
はgoalを返す関数
(reify v)
: 変数v
をreifyする関数を返す- その関数にstreamを適用すると
v
をreifyしたものが返される (walk* v s)
: 変数v
をリスト内部までsubstitutions
で置き換える(reify-s v r)
: 値v
に現れるfreshな変数をreified variableに置き換えるためのreified-name substitutionを返す(reify-name n)
:_0
のような変数名を作る
- その関数にstreamを適用すると
(run-goal n g)
: goalg
を満たすsubstitutionをn
個まで返す(take-inf n s-inf)
: streams-inf
の先頭からn
個の要素を返す
まだ手ごたえがちょっと不足しているんだがどうすれば