いろいろな組み込み関数
12.1 項の型チェック
項には定数、文字定数、変数、構造などの型がありました。また、変数は実行中のある時点においてユニファイされて具体的な値が代入されているか、または代入されていないかのどちらかの状態が考えられます。ということは、変数がユニファイされていれば、その変数の具体的な値にも型があるわけです。
そのような項の型が判ると便利です。また、整数かどうかも判ると便利です。例えば、エラーチェックを考えてください。ある変数が整数なら続けて処理を行うが、整数でなければメッセージを出力するなどの処理をしたい場合などです。このようなチェックを行う関数には次のようなものがあります。
- var(X)
Xがユニファイされていない変数のとき、真となります。
- nonvar(X)
Xが変数以外か、または既にユニファイされた変数のとき、真となります。
- atom(X)
Xがアトムのとき、真となります。
- integer(X)
Xが整数のとき、真となります。
- atomic(X)
Xがアトムか整数のとき、真となります。
さて、アトムとは何でしょう。これは、いままで私たちが「文字定数」と呼んできたものなのです。正確には「アトム」と「文字定数」とは微妙なニュアンスの違いがあるのですが、まぁ、いっしょと考えても差し障りありませんので、皆さんには馴染みのある「文字定数」という言葉を使ってきました。今後も学習を続けていく際には「文字定数」でも構いません。ただ、Prolog関係の本を読んだり、また今回のような型チェックの関数を使う場合には「文字定数」=「アトム」と置き換えて考えてください。
《演習問題》
- 磯野家の家族関係のプログラムにおいて上のvar,nonvar,atom,integer,atomicのそれぞれが真および偽となるようなケースを挙げなさい。必要なら適当な節を追加すること。
12.2 項の組立と分解
新しい項を作ったり、項を分解したりする関数があります。
- Struct =.. List
右辺にリストList、左辺に構造Structを指定し、変換します。
?- f(a,b) =.. L.
L=[f,a,b]
?- T =.. [parent,sazae,tara].
T=parent(sazae,tara)
- functor(Term,Name,Arity)
項Termの関数子をName、アリティをArityとする。
?- functor(food(a,b,c),N,A).
N=food
A=3
《演習問題》
- 磯野家の家族関係においてparent節をリストに分解してファイルに書き込みなさい。繰り返しの処理はまだ学習していないので、1つづつ実行すること。
12.3 項の等価
前に算術計算で数の比較について学習しました。一般に比較するのは数の場合が多いのですが、文字定数や構造を比較したい場合もあります。そこで、項が等しいかどうかを比較するものとして、次の記号を用います。
- A == B
項AとBが等しいとき、真となる。
- A \== B
項AとBが等しくないとき、真となる。
=:=と==と=とが混乱しないよう注意してください。例を挙げておきます。
?- f(a,b) == f(a,b).
yes
?- f(a) == f(X).
no
?- g(f(X)) == g(f(X)).
yes
?- X \== Y.
yes
?- f(a,b) = f(a,b).
yes
?- f(a) = f(X).
X = a
yes
12.4 データ処理
磯野家の家族関係のようなデータベース的な処理はPrologの得意なものの一つです。磯野家の家族関係の場合は、必要な節は全てファイルに記述しておいて、質問をプロンプトから入力しました。でも、プロンプトから節を入力したいこともあるかもしれませんし、実行中に条件によって節を増やすという操作をプログラムの中に書きたいこともあるかもしれません。逆に、節を削除したいこともあるでしょう。そのような処理を行う関数には次のようなものがあります。
- assert(X)
節Xをプログラムに付け加える。同じ関数子、アリティ(引数の数)の節が既に存在している場合は、その最後に付け加えられるようにしている処理系が多い。このあたりは処理系に依存するのでマニュアルで確認すること。
- asserta(X)
節Xを同じ関数子、アリティ(引数の数)の節が既に存在している場合は、その最初に付け加えられる。
- assertz(X)
節X同じ関数子、アリティ(引数の数)の節が既に存在している場合は、その最後に付け加えられる。
- retract(Y)
Yと節の頭部がユニファイする節を消去する。
- abolish(Z)
関数子Zの節を全て消去する。但し、処理系によってはabolish(Z,N)とし、関数子とアリティを引数としているものもある。
先ず、簡単な具体例を示しましょう。
?- holiday.
no
?- assert(holiday).
yes
?- holiday.
yes
?- retract(holiday).
yes
?- holiday.
no
途中でlisting.と入力してインタプリタに読み込まれているプログラムの内容を確認すると一層判りやすいでしょう。
次に、もう少し複雑な例を示します。以下のプログラムがあって、インタプリタに読み込まれているとします。
nice:-
holiday,\+(school).
mistake:-
holiday,school.
hard:-
school,study.
school.
study.
このプログラムとは、以下のような対話ができます。
?- nice.
no
?- hard.
yes
?- retract(study).
yes
?- hard.
no
?- assert(holiday).
yes
?- mistake.
yes
?- retract(school).
yes
?- nice.
yes
更にもう少し複雑な例を示します。遊幽のときに次の節がありました。
tall(yuusuke).
small(genkai).
small(koenma).
tall(kurama).
small(hiei).
このプログラムに次のようにルールを加えてみましょう。
?- assert((taller(X,Y):-tall(X),small(Y))).
yes
?- taller(A,B).
A=yuusuke
B=genkai
?- abolish(small).
yes
?- taller(yuusuke,_).
no
これらの関数については、もっと気の利いた使い方もできます。ルールにマッチングさせて答えを出すより、予め答えの節を作成しておいた方が操作する場合に速いに決まってます。そこで、次のプログラムを考えます。
kukutable:-
member(X,[1,2,3,4,5,6,7,8,9]),
member(Y,[1,2,3,4,5,6,7,8,9]),
Z is X * Y,
assert(kuku(X,Y,Z)),
fail.
failは強制的に失敗させるという命令です。つまり、assertを実行した後で必ず失敗するということで、ここでバックトラックを生じさせるのです。実際にどのように動くのかはトレースしてみてください。このプログラムを読み込んで実行します。
?- kukutable.
これは勿論成功しませんが、実行の過程でどんどんkukuの節をassertしていきます。このようなプログラムの成功・失敗に関わらず行われる処理を{\gt 副作用}と呼びます。上のプログラムで掛け算のデータベースを作成すると、次のように掛け算の結果を求めたり、特定の値になる九九を求めることができます。
?- kuku(3,4,X).
X=12
?- kuku(6,X,12).
X=3
?- kuku(X,Y,12).
X=2
Y=6;
X=3
Y=4;
.....
《演習問題》
- 九九のデータベースから全ての節を一度に消去する操作を行いなさい。
- 九九のデータベースから答えが12の節のみ消去する操作を行いなさい。
12.5 プログラムの制御
前節でfailを使って強制的に失敗させるというのをやりました。これによって結果的にループを実現したわけです。処理の流れを制御する目標には以下のようなものがあります。
- fail
強制的に失敗させる。
- true
強制的に成功させる。
- not(P)または\+(P)
Pが失敗のとき、真となる。
- call(P)またはP
目標Pを起動する。前節でPrologインタプリタのプロンプトからmaketable.と入力したのがそれにあたる。
- repeat
繰り返しを行う。以下に詳しく説明する。
- !
カットと呼ぶ。正式にはバックトラックの抑制を行うが、repeatとともに以下に説明する。
実は、Prologの処理の制御には奥深いものがあります。(判りやすく言うと、とても難しいってことね)カットを用いたバックトラックの抑制に関しては、ここではあまりつっこまないでおきましょう。(学生諸君の学習意欲を阻害するつもりはありませんが、なにせ「記号論理学」の単位は2単位しかないから。ここで上っ面だけの理解になったにしても、最後のプロダクションシステムまで進んで欲しいから。もっと頑張りたい人は申告してください。別途対応しましょう)
repeatとカットに関しては以下の典型的な処理だけは理解しておくようにしてください。
条件が成立するまで処理を繰り返す
repeat,処理,条件,!.
file_read:-
repeat,
read(X), % 処理
write(X),nl, % 処理
X == stop, % 条件
!.
《演習問題》
- 磯野家の家族関係のparent節をリストに変換してファイルに書き出す処理を繰り返しを用いて書き直しなさい。
- ファイルに書き出したparentのリストを読み込んで節に変換するプログラムを書きなさい。
12.6 該当者を捜せ
いろいろな組み込み関数を取り上げてきました。最後に、ある条件に該当する節をピックアップする関数を紹介します。データ処理を行うときに重宝します。
- bagof(P,X,L)
目標Pが成立するようなXのリストをLとする。
- setof(P,X,L)
bagofと同様にリストLを作成する。但し、Lを昇順にソートする。
これらは例を参照した方が判りやすいでしょう。例えば、次のように使います。再び遊幽で、
tall(yuusuke).
small(genkai).
small(koenma).
tall(kurama).
small(hiei).
でしたね。ここで背の低い人(?)だけピックアップしてみましょう。
?- bagof(Person,small(Person),List).
List=[genkai,koenma,hiei]
Listはsmall(Person)を成立させる、即ちsmall(Person)という節が存在するようなPersonのリストということになります。では、目標Pの引数が2つ以上のときはどうなるのでしょう。磯野家で例を示します。
?- bagof(Person,parent(Person,_),List).
List=[namihei,fune,sazae,masuo,umi]
《演習問題》
- 磯野家の家族関係において、子供のいる人(親という立場にある人)のリストを引数とする節parents(List)を作成するルールを作りなさい。実行して確認すること。
- 同様に親のいる人(親が存命中の人ということね)のリストを引数とする節children(List)を作成するルールを作りなさい。
- 遊幽で、人間ではない人(?)のリストを求めるルールを作成しなさい。
目次に戻る