Prologの基本的なデータ構造

Prologの基本的なデータ構造

  • 単位節について
  Prologの基本単位は「項(term)」と呼ばれます。
  項は単純項複合項に分類されます。
  さらに単純項は定数変数があります。

  • 定数
定数はプログラムの実行中に値は変化せずに一定です。
定数には数とアトムがあります。
数    ・・1234など
アトム・・abc、山田、'XYZ'など

  • 変数
変数はプログラムの実行前に、自身の値が定まっていない項です。
プログラムの実行中に値が決まります。
(1)英大文字で始まる任意の文字列       ・・・XYZ
(2)アンダーラインで始まる任意の文字列 ・・・_山田

  • 複合項
複合項は項をいくつか組み合わせてできた項です。
述語(アリティ、・・).
※Prologでは引数のことをアリティを呼びます。

単位節を使ってみよう(その1)

a(1).
test:- a(X),write(X).

?-test.
1
  • 3つのルール
  上記の実行例で、[test:-・・・]の記述がありますが、これはPrologにおいて「規則」と呼ばれます。
  また[?-test.]は「質問」と呼ばれ、記述した述語を実行するルールです。

 Prologには「事実」「規則」「質問」の3つのルールを用いて知識を表現できます。
事実・・・a(1).
規則・・・test:- a(1).
質問・・・?-test.

ユニフィケーションについて

  test述語を実行した際、a(X)が実行されます。
  その際、単位節[a/1(/1はアリティ数)]が実行され、X変数に[1]が代入されます。
  この仕組みを、ユニフィケーションと呼びます。

単位節を使ってみよう(その2)

a(1,2).
test:-a(X,_),write(X).
1

a(X,Y).
test:-a(X,Y),write(X=Y).
_10=_11
 ※単位節[a(X,Y)]には定数ではなく、変数が定義されてますので、
  test述語を実行した際、a(X,Y).から取得した値は未定義(変数)のため
  write述語で実行した結果、未定義の値が出力されます。
  よって、必ずしも[_10=_11]といった値にならないことに、注意してください。

a(1).
a(1,2).       %こちらの単位節が呼ばれる
test:-a(X,_),write(X).
1

a(1,2).
a(3,4).
test:-a(X,_),write(X).     %a(3,4).は実行されない。
1
 上記4の実行時、単位節「a/2」の中で、a(1,2)の単位節のみ実行された。
 a(3,4).も表示したい場合は、どうするか?
 ここでバックトラックというPrologの特殊な機能を使用します。

バックトラックについて

 バックトラックとはプログラム実行時、ある述語の別解を求めるための制御構造です。
a(1).
a(2).
?-a(X),write(X),nl. %nlは改行する述語です
1;・・・ここで[;]と打ちEnterを押し別解を求める。
2;
no

 単位節[a/1]の解を質問によって求めた際、まずa(X)のXには[1]がユニフィケーションされ
 X=1が解として出力されます。この際、セミコロン[;]を入力しEnterを押すと、[1]の別解を
 求められ(バックトラックし)、[2]がユニフィケーションされ、X=2が出力されます。
 またX=2が出力された後、もう一度バックトラックさせると、他の解は存在しないため、[no]と
 表示し終了します。

 別解を求めるたびに、[;]を入力するのは手間なので、自動的にバックトラックをさせる述語がPrologには存在します。
 それが[fail]という述語です。
a(1).
a(2).
?-a(X),write(X),nl,fail.
1
2
no

これで、上記4の実行例において、a(3,4)を表示する方法が理解して頂けたかと思います。

4・(改修)
a(1,2).
a(3,4).
test:-a(X,_),write(X),nl,fail. %failでバックトラックさせる
test:-!.
1
3
yes

カットオペレータについて

バックトラックを使って、a/2単位節のすべてのデータを表示する仕組みを説明しました。

しかし、プログラムの制御によってはある単位節のデータを処理した後は、その後の
データは必要なく、処理をしてほしくない場合があるかと思います。

その際に、使うPrologの述語が「!(カットオペレータ」です。

例を示します。
a(1).
a(2).
a(3).
test:- a(X),write(X),nl,fail.

という事実と規則を定義した場合、test述語を実行すると、単位節[a/1]のデータが
すべて出力されることは、先ほど学習しました。

ではa(2)を出力した後は、a(3)を出力したくない場合は、どのようにすればよいか。

以下の用にカットオペレータを使います。
a(1).
a(2):- !.
a(3).
test:- a(X),write(X),nl,fail.

では実行してみます。
?-test.
1
2
期待した出力結果が出ましたでしょうか?

バックトラックを制御する際に、カットオペレータは使用しますので、
覚えておいてください。

単位節を使ってみよう(よくある間違え)

 単位節で、
 a('A').
 b('B').
 c('C').
 という事実があり、それぞれの値を表示する場合は、どうすれば良いでしょうか? 
 よくある間違えとして、こういう実装をしてしまう場合があります。

test:-
      a(X),write(X),nl,
      b(X),write(X),nl,
      c(X),write(X),nl.
      
?-test.
A
no.
 上記の述語を実行してみると、Aのみが出力され、その先は実行されずfailしました。
 これは何故でしょうか?

 実はPrologにおいて、変数は1度しかユニフィケーションされません。
 よって、a(X)でXの値が['A']にユニフィケーションされた後、b(X)が実行されますが
 これは[b('A')]を実行された事と同一になります。
 b('A')の単位節は存在しない(定義されていない)ため、failされたため、上記の通りの
 実行結果となりました。

 a,b,cのすべてのデータを表示するには下記のようになります。
test:-
      a(X),write(X),nl,
      b(Y),write(Y),nl,  %X以外の変数
      c(Z),write(Z),nl.  %X、Y以外の変数
      
?-test.
A
B
C
yes.
 これで、a,b,cのすべての単位節のデータが表示されました。

まとめ

今回は以下の事を学習しました。
  • Prologのデータ構造(単純項、複合項など)
  • ユニフィケーション
  • バックトラック
  • カットオペレータ

ここはPrologを扱うにおいて、基本的なことなので身に着けておいてください。
最終更新:2014年05月07日 12:26