・規則の導入
プロログでは、プログラム内の事実を用いた規則を導入することができる。
以下のようなプログラムを与える
parent(gf,f).
parent(gm,f).
parent(f,c1).
parent(m,c1).
parent(f,c2).
parent(m,c2).
この家族関係を示すプログラムに、
offspring(X,Y).
XはYの子供かどうかを示すプログラムを追加する場合。
XがYの子供であるということは、YはXの親であることと同一である。
よって書き方を変えると
parent(Y,X).
ということになる。
これはどのようなX、Yでも適合するルール(すなわち、XがYの子供=YはXの親)である。
このルールをプログラムで表現すると
offspring(X,Y) :- parent(Y,X).
というあらわし方をする。
・規則の読み方
parent(gf,f).
parent(gm,f).
parent(f,c1).
parent(m,c1).
parent(f,c2).
parent(m,c2).
offspring(X,Y) :- parent(Y,X).
というプログラムが与えられているとき、c1がfの子供であるかどうかを質問する。
offspring(c1,f).
このとき、この質問内容が真になるのは、後ろの
parent(Y,X).
が真であるときのみである。
読み方としては
parent(Y,X) が真 ならば offspring(X,Y) は真 である。
というかんじ。
後述部を条件部、前述部を結論部という。
規則で重要なのは
条件部が真ならば結論部もまた真なり、ということ。
・プログラムにおける”かつ”の導入
前回のまとめで、連関質問で”かつ”を使った。
これをプログラムにおいても私用することが可能である。
parent(gf,f).
parent(gm,f).
parent(f,c1).
parent(m,c1).
parent(f,c2).
parent(m,c2).
sex(gf,male).
sex(gm,female).
sex(f,male).
sex(m,female).
sex(c1,male).
sex(c2,female).
このような家族関係と、その性別の事実を書いたプログラムがある。
このプログラムに、父親を定義する規則を導入したい。
XがYの父親である ということは YはXの親である かつ Xは男性である
というプログラムがかければいいわけであるから
father(X,Y) :- parent(Y,X),sex(X,male).
とすればよい。
このとき”かつ”は連関質問のときと同じように","を使う。
・プロログの変数の扱い(追加)
プロログの変数のスコープは、そのプログラム内の節のみである。
どういうことかというと
red(X) :- fruit(apple).
と
red(X) :- salad(tomato).
のXはまったく別物であるということである。
変数はその一行の中でのみ有効である。
無名変数について
プロログには無名変数というものが存在する。
無名変数とはその名のとおり、名前のない変数である。
どういったときに使うか
Xは子供がいるか ということは Xは”誰かの”親である
というような規則を使いたいときなどがある。
条件部では、Xには誰かしら子供がいればよいわけで、いつものようにYなどの変数を用いても結果は変わらない。
しかし、このとき知りたいのは子供がいるかどうかである。
そこで無名変数を導入する。
haschild(X) :- parent(X,_).
無名変数はアンダーバーで示す。
こうすると、無名変数の部分は主力結果には表示されないようになる。
・ユニフィケーションについて
プロログにはユニフィケーションという機能がある。
ユニフィケーションとは同一などの意味を持つ。
ユニフィケーションってなにをやるの?
ユニフィケーションは、与えられた二つの情報が同値かどうかを確認するものである。
たとえば
A = xyz.
のように質問したとする。
このとき、左辺と右辺が同一となるのは、変数Aが定数xyzであったときのみである。
よって出力は
A = xyz.
yes
である。
Aがxyzであったときのみ同一(ユニファイ)ということである。
関数とユニフィケーション
関数を用いてもユニフィケーションを行うことができる。
date(2007,9,26) = date(Year,9,25).
この場合はYearが2007であったときのみユニファイである。
しかし
date(2007,9,26) = date(2007,9,27).
この場合は、左辺と右辺はどうやっても同一にはならない・・・
26も27も定数であり、変数のように代入によって同一化できないから。
変数とユニフィケーション
両辺に変数が合ってもユニファイできる。
date(9,X) = date(9,Y).
この場合、X=YかY=Xのときは同一である。
出力は
X=Y
yes
となる。両方が変数であっても同値であると判断できる。
定数とユニフィケーション
両辺が定数であった場合、両辺が同じ定数でなければユニファイできない。
2 = 4 もちろんユニファイできない
2 = 2 これならユニファイ可能
関数ユニフィケーションの条件
関数ユニフィケーションの条件として、
右辺と左辺の関数が同じ関数であること
がある。
また、
point(3,5) = point(dot(1,5),5).
はユニファイである。
すなわち
dot(1,5)
が3であればユニファイであるということ。
・トレースについて
プロログにはデバッグを助ける機能であるトレースがある。
トレースオンにするには
trace.
トレースオフにするには
notrace.
と入力すればよい
・プロログにおける再帰的プログラミング
parent(gf,f).
parent(gm,f).
parent(f,c1).
parent(m,c1).
parent(f,c2).
parent(m,c2).
このような家族関係が与えられているとき、先祖を調べるプログラムを追加したい。
XがZの先祖である ということは XはZの親である
ということがまずあげられる。しかし、先祖であるということは、その親の代の親も先祖であり、またその親の親も・・・となるのでこれだけでは不十分である。
すると
XがZの先祖である ということは XはYの親である かつ YはZの先祖である
という一文を追加すればよい。
senzo(X,Z) :- parent(X,Z).
senzo(X,Z) :- parent(X,Y) , senzo(Y,Z).
この2文で再帰的呼び出しが完成する。
手順を追ってみる。
例1
gfはfの先祖かどうかを質問する
senzo(gf,f).
プログラム内のsenzo(X,Z) :- parent(X,Z).を探索する。
するとparent(gf,f).という事実がプログラム内に存在するので、gfはfの先祖であることが真となる。
例2
gfはc2の先祖かどうか質問する
senzo(gf,c2).
プログラム内のsenzo(X,Z) :- parent(X,Z).を探索する。
しかし、parent(gf,c2)という事実がないのでひとつ戻り、
senzo(X,Z) :- parent(X,Y) , senzo(Y,Z).を参照する。
すると
parent(gf,Y)を探索し、このときのYがfであることがわかる。
Yにfが代入され、次のsenzo(Y,c2) すなわち senzo(f,c2).
を探索。
senzo(f,c2) :- parent(f,c2) の探索を行い、
事実が存在するので真を返す。
すると
senzo(X,Z) :- parent(X,Y) , senzo(Y,Z).
において、条件部がともに真を返すので真の値をとる。
ことになる。
再帰的呼び出しになっているので、先祖関係の探索をこの2行だけで行うことが可能になる。
以上