geniacllm/wiki40b huggingface dataset:
複合項の中で特別な扱いを受けているものとしてリストがあり、LISP以来の記号処理プログラミングの伝統に則りPrologでも極めて多用される。実際のところ、Prologのデータ構造は単位節定義とリスト以外にはないと言っても過言ではない。リスト'はいくつかの項を順に並べたもので、その先頭要素を取り出せば、残りはまたリストであるというように再帰的である。例えば [a,b,5] のように、要素となる項を「,」で区切り「[」と「]」で囲った形で表現する。要素のないリストは [] と表記し、空リスト、あるいは nil と呼ぶ。リストをグラフとして示すと、リスト [a,b,5] の構造は      . ------ . ------ . ------[]      |        |        |      a        b        5のようになるだろう。Prologのリストの表記として、要素を"|"で区切る方法がある。この記法があるためにPrologのリスト処理は視覚的で読みやすい。先頭からいくつかの要素の後に"|"が来て、その後にはリストか[]が来る。 例： [a,b,c,5,6] は、先頭の要素 a,bと残りの要素  [c,5,6]  をつなげた  [a,b|[c,5,6]]  と等価である。 ただし、[[a,b]|[c,5,6]]ではない。Prologの複雑なリスト処理をそれでも宣言的と見なすことができるのは、専らこの記法あってのことである。この記法はPrologのプログラムではリストを先頭要素と残りリストに分解する場合に多用される。[1,2,3]=[H|R]の場合、Hは単一の項(複合項であることも含めて)を表すパターンだから、H=1,R=[2,3] に分解される。後に示されるプログラム例の章には、リスト要素の加算,append,組合せ,クイックソート 他、多数の事例がある。重複するからここでは二例だけを示す。member(H,[H|T]).member(H,[_|T]) :- member(H,T).append([],L,L).append([H|X],L2,[H|Z]) :- append(T,L2,Z).?-  member(H,[1,2,3]).H = 1;H = 2;H = 3 .Prologを代表する述語 member/2 の[H|T]と[_|T] と append/3の[H|X]と[H|Z] の所にこの記法が使われている。 ?- member(H,[1,2,3]). にあっては、第一番目の定義節から [1,2,3] が [1|[2,3]] に分解できて H = 1, T = [2,3] となるから、最初の解であるH = 1が表示されるのである。以下では、二つのリストを単一化することを通して、リスト記法の各部分がどのような関係にあるかの理解を深めよう。?- [a,b,c,5,6] = <nowiki>[a,b|[c,5,6]]</nowiki>.true.?- L = [c,5,6],   [a,b,c,5,6] = [a,b|L].true.?- [a,b,c,5,6] = <nowiki>[a,b,c,5,6|[]]</nowiki>.    % |の後に[]が来る。true.?- [a,b,c,5,6] = <nowiki>[[a,b]|[c,5,6]]</nowiki>.false.?- [a,b,c,5,6] = [a,b|c,5,6].false.最後の例の[a,b|c,5,6]のc,5,6はリストと看做されない。'|' を使ってリストを区切る用法もグラフ化すると、リスト [a|[b,c,5,6]] = [H|T] の構造は    [ H   |    T ]      . --|--- . ------ . ------ . ----- . -----[]      |        |        |        |       |      a        b        c        5       6である。リストは簡単に成長させることができる。リストを成長させる(_追加要素,_リスト,[_追加要素|_リスト]).?- リストを成長させる(3,[1,2],L).L = [3,1,2].リストを成長させる/3では単一化のからくりを巧みに使って、リストの先頭に要素を追加している。リストの要素をひとつ切り取るには、反対にリストの要素をひとつ切り取る([_切り取る要素|_リスト],_リスト).?- リストの要素をひとつ切り取る([1,2,3],L).L = [2,3].リスト要素の切り貼りはこのようなパターンで行われる。リストは先頭から要素を加え、先頭から要素を検査し、先頭から要素を取り去るのに適したデータ構造を持っている。ここまで示してきた通り、リストは読みやすいように特別な表記法を与えられた複合項であるが、実は一般の複合項と同様の構造で実現されている。リストは関数子名が'.' と決められていて、以下の例のように実現されたアリティが2の複合項である。 例：  [a,b]  は複合項  '.'(a, '.'(b, []))  と等価である。?- [a,b] = '.'(a, '.'(b, [])).true.形式記述言語の多くがそうであるように、Prologはその制御の大半が再帰処理によっている。リストは再帰的な構造データの中でも最も簡素で扱いやすいものであり、制御構造とデータ構造の一致という点からもリストが多用される十分な理由がある。複合項もまた再帰的構造データではあるが、生成、分解、置換などの際の扱いが複雑になるため、グラフやオートマトンなどの定義/表示以外にはあまり使われない。'.'(a,'.'(b,[]))の構造で分る通り、リストも実は複合項である。リストは生成、分解、置換などが容易くできる構造を持つ特別な複合項であり、それ故に特別な表記法を与えて、さらなる便宜を供しているのである。Prologではリストの内包表記はできない。setof や findall の表現が意味的にそれに近いが、ここでの表記をリストを表す項として、遅延して評価するために持ち回ることはできない。例えば?- findall(N,(member(N,[1,2,3,4]),0 is N mod 2)),L1),   append(L1,[8,10],L).L1 = [2,4],L = [2,4,8,10].であるが、findallを関数表現として、?- L1 = findall(N,member(N,[1,2,3,4]),0 is N mod 2)),   append(L1,[8,10],L).と表記したとしても、この項だけ例外的に単一化を免れ関数評価する特別な機構を付加しない限り、この第一引数はリストと看做されることはなく、エラーとなり、Lに期待する [2,4,8,10] は得られない。このことから単一化がリストの内包表記を阻んでいる理由の一つであることが解る。Prologには集合を表す特別な表現がなく、リストでこれを代用するのが普通である。この問題については、Prologプログラミングの章で詳述する。
Prologの実行は述語を定義された処理系に対してユーザが質問することによってなされる。質問とは、?- 親子(ふね,タラオ).のようなものである。ここでの質問は「ふねはタラオの親か」という意味だ。「ふねとタラオは親子関係である」という読み方もある。このような質問に処理系が答えることができるためにはその知識が必要である。Prologではこの知識を述語という形式で与える。そのことを述語を定義するという。定義された述語には一般に処理系によって最低限必要なものとしてユーザに対してあらかじめ用意された組込述語(ユーザは定義しなくてもよい)と、ユーザが自ら定義したユーザ定義述語の二種類がある。ユーザは自ら定義した述語群をファイルに保存して、そのファイルを組込述語であるconsult/1述語によって処理系に読み込ませる。サザエさんの家系図に関する述語群が親子(波平,サザエ).親子(ふね,サザエ).親子(波平,カツオ).親子(ふね,カツオ).親子(波平,ワカメ).親子(ふね,ワカメ).親子(マスオ,タラオ).親子(サザエ,タラオ).夫婦(波平,ふね).夫婦(マスオ,サザエ).member(X,[X|_]).member(X,[_|R]) :-    member(X,R).上記のように、エディタなどで書かれて、ファイル'sazaesan.pl'に存在するとして、?- consult('sazaesan.pl').true.を実行することによって、サザエさんの家系図に関する述語群がユーザから参照できるようになる。?-はプロンプトと呼ばれ、質問を受け付ける準備ができていることを示す。プログラムを実行するのには、一般にProlog処理系の起動後、最初のプロンプトが表示されてからユーザ自身で質問(目標)という形でプログラムを実行する。実務的なプログラムではこの質問によって述語定義の融合、頭部の単一化、本体部の導出の長い連鎖となり、最終的にその質問が真か偽の結果を残して終了する。これが普通の使い方だが、処理系の起動時にコマンド引数などで最初に実行する質問を引き渡して、起動後停止することなく、質問が実行される場合もある。ここでlistingという質問を与えてみる。この組込述語は現在実行可能な状態にある述語すべてのソースコードを表示する。?- listing.親子(波平,サザエ).親子(ふね,サザエ).親子(波平,カツオ).親子(ふね,カツオ).親子(波平,ワカメ).親子(ふね,ワカメ).親子(マスオ,タラオ).親子(サザエ,タラオ).夫婦(波平,ふね).夫婦(マスオ,サザエ).member(X,[X|_]).member(X,[_|R]) :-    member(X,R).このように定義済みであることが分かった。これが先の?- consult('sazaesan.pl').の効果である。最初の親子(波平,サザエ). から 夫婦(マスオ,サザエ). までが事実であり、形式的には本体がなく、単位節と呼ばれる。その下の member は二番目の節に再帰的に本体があり、これはルールと呼ばれる。ルールmemberを使った質問をしてみる。?- member(サザエ, [波平,サザエ,マスオ]).true.となる。サザエは集合{波平 サザエ マスオ} の要素であるかという質問に真と答えている。さらに簡単な質問をしてみる。単に事実を問うものだ。?- 親子(波平,サザエ).true.?- 親子(ふね,X).X = サザエ;X = カツオ;X = ワカメ .?-この質問で第二引数に X 乃ち論理変数が使われた。処理系はこのXに適切な値が入ることで、この質問を真となって終わらせようとする。X に カツオが入った時、?- 親子(ふね,カツオ).true.で真となるし、ワカメが入った時、?- 親子(ふね,ワカメ).true.で真となる。この二つの質問を 論理変数 X を順に束縛することで満たしているのが上の ?- 親子(ふね,X). での実行結果である。質問の詳しい説明は後のプログラム例「家系図」以下にある。
Prologの処理系は質問がなされ、それに回答を繰返すことによって処理が進むという作りになっている。しかし、質問することなしに、処理系の起動時にプログラムを実行することももちろんできる。最初に現在処理系で使われている代表的な二つの方法を示す。1)    ソースプログラムの中に起動する質問を記述する。    ・ 処理系の起動時に -f ファイル名 オプションを指定して、ファイル名のソースファイルを読みこませる。    ・ ソースプログラムの中に、:- <<目標>>.のように自動実行する質問を記述する。2)    処理系の起動時に -f ファイル名 オプションを指定すると共に、-t オプション等で、最初の質問の述語名を直接指定する。(例えば mainなど )　　 ・   # prolog -f sazaesan.pl -t main必ずしも処理系起動時と限らないのだが、consultされるファイルの途中に特別な述語 :- を指定して、自動で質問が実行できるようになっている処理系が多い。ファイル'sazaesan.pl'の第一行目に:- write('%%% サザエさん家系図の読み込み %%%\n').親子(波平,サザエ).親子(ふね,サザエ).親子(波平,カツオ).親子(ふね,カツオ).親子(波平,ワカメ).親子(ふね,ワカメ).親子(マスオ,タラオ).親子(サザエ,タラオ).夫婦(波平,ふね).夫婦(マスオ,サザエ).member(X,[X|_]).member(X,[_|R]) :-    member(X,R).が書かれているとすれば、?- consult('sazaesan.pl').を実行した直後に%%% サザエさん家系図の読み込み %%%と表示される。'sazaesan.pl'の第一行が読み込まれ、処理系が :- から始まる特殊な節を見つけると、?- write('%%% サザエさん家系図の読み込み %%%\n').という質問をユーザからの入力なしに実行する。この機能を利用して、処理系の起動時にコマンドラインに -f オプションなどで初期読み込み述語ファイル名が指定できる作りになっている処理系が多く、# prolog -f sazaesan.plこの ':-' 述語をファイル内の適宜な場所に記述することによって、質問の自動起動が可能となる。しかしながら質問で変数束縛の状態表示を期待している場合は、質問ー応答モードを脱して動いてしまっているから、:-以下での質問に対する実行が完了した場合でも質問ー応答する場合のようにはうまくいかない。";"や改行待ちとなる非決定性の制御にも移行しない。このような変数束縛の表示はwriteのような出力述語を続けて記述してユーザが表示させる必要があるだろう。自動実行を理解しやすくするために、'sazaesan.pl'に少し :- 節を追加してみる。:- write('%%% サザエさん家系図の読み込み %%%\n').親子(波平,サザエ).親子(ふね,サザエ).親子(波平,カツオ).親子(ふね,カツオ).親子(波平,ワカメ).親子(ふね,ワカメ).親子(マスオ,タラオ).親子(サザエ,タラオ).夫婦(波平,ふね).夫婦(マスオ,サザエ).member(X,[X|_]).member(X,[_|R]) :-    member(X,R).:- write('%%% 波平の子供は %%%\n').:- member(波平,X),writef('%t\n',[X]),fail;true.:- write('%%% 終了します %%%\n').:- halt.これを'sazaesan.pl'とは別のファイル'temp1.pro'に書いて置くとする。組込述語 writef/2 や fail;true 制御についてここでは詳しくは述べないが、最後に処理系を終了させる組込述語 halt. を実行させることにより、# prolog -f temp1.pro% library(swi_hooks) compiled into pce_swi_hooks 0.00 sec, 2,224 bytes%%% サザエさん家系図の読み込み %%%%%% 波平の子供は %%%サザエカツオワカメ%%% 終了します %%%#のような、バッチ処理プログラムとして実行することができる。起動述語として例えばプログラムの起動時コマンドオプションで -t main を指定する場合は、予め、・・・・・夫婦(波平,ふね).夫婦(マスオ,サザエ).member(X,[X|_]).member(X,[_|R]) :-    member(X,R).main :- member(波平,X),writef('%t\n',[X]),fail;true.のように述語として定義して置けばよい。組込述語 consult/1 の事例を示したが、Prologの処理系は共通の組込述語群とその処理系独自の組込述語群を持っており、後者が統一されない状態であることは歴史の中で触れた。ユーザは各処理系のマニュアルを注意深く読む必要がある。
Prologでプログラムを記述する単位は述語（英: predicate）で、他の言語での関数やサブルーチンに相当する。つまり、Prologプログラムは述語の集まりで、述語はあるまとまった機能を表現している。述語は1つ以上の節（英: clause）と呼ばれる項からできている。節は以下の形をしている。頭部 :- 副目標₁, ..., 副目標ₙ.あるいは、頭部.1つの述語に属する節は、同じ述語名(関数子名)と引数の数(アリティ)を持つ頭部からできている。述語名とアリティが異なれば別の述語とみなすため、述語を指定するときは"述語名/アリティ"と表記されることもある (例： member/2) 。1つの述語は成功、あるいは失敗のいずれかの結果を返す。副目標₁〜副目標ₙの間の "," はANDを意味する演算子であり、1つの節が成功するのは本体部がすべて成功した場合である。本体部の実行は、副目標がANDの関係で連接する場合、記述された順に行われる。頭部のみの節は 頭部 :- true. と同じ意味であり:-trueが省略された形式だと考えればよい。1つの述語が複数の節からなる場合、上から順に実行され、どれかが成功したら述語自体は成功する。つまり同じ述語内の節の関係はORの関係となる。節同士はOR関係であり、それぞれ情報の共有という観点からは完全に独立している。一つの節の中の副目標から、同じ述語の別の節中の情報にアクセスするためには、新たにこの述語を目標(質問)として呼び出す必要があり、この場合、制御や変数束縛等は完全に初期状態での実行となる。一度trueになった節で一旦は変数に値が束縛(代入)されていたとしても、バックトラックして、それより下の節に制御が移った場合は、制御の移った節からバックトラックした(すなわち偽となった)節の変数束縛を利用することはできない。述語は親となる目標(副目標)によって謂わば質問として呼び出される。これに対して、述語の各節は予め備えている状態にある。質問されることによって、頭部の単一化を行い、これに成功した節の、本体の副目標を順次、今度はこれが親となって質問する。そういう備えができている。それが述語が定義されているということである。
Prologの基本的なアイデアは、ホーン節をプログラムと見なして実行する、ということであるため、純粋なPrologのプログラムは手続き的にもホーン節に従って宣言的にも解釈ができる。1つの節の解釈は以下のようになる。"副目標₁"かつ ...、かつ"副目標ₙ"が真であれば、"頭部"は真である (宣言的意味)"頭部"であることを示すには、"副目標₁"かつ...、かつ"副目標ₙ"を示す (手続き的意味)手続き的に見ると、"副目標₁"〜"副目標ₙ"がすべて成功する場合、節は本体部を順番に実行する関数やサブルーチンのように見なせ、再帰呼び出し可能な手続き型/関数型言語と動きはさほど違わない。他の言語との大きな違いは、本体部のいずれかが失敗した場合、最後に選択した節にバックトラックし次の節から再度実行を続けることである。Prologの動作をプログラムが指定した条件での解の探索として見ると、深さ優先の解探索ととらえることができる。
C言語など通常のプログラミング言語の変数は値の格納場所であって、計算が進むに従って内容が変化する。Prolog などの論理型言語での変数は数学的な変数に近いもので、何らかの値につけた名前である。値は決まっているか決まっていないか(代入されているか代入されていないか)のいずれかで、一度決まってしまえば値が他の値で置き換わることはない。値が変わるのはバックトラックにより代入が解かれた後に再度値が決まった(代入された)場合のみである。この変数は他のプログラム言語のそれのようにプログラム中に宣言することはできず、述語或いは述語呼び出しの引数の位置にのみ現れる。通常のプログラミング言語の変数と区別するために論理変数と呼ばれることもある。論理変数が述語または質問の引数の位置にのみ現れるという意味を理解しやすくするために以下の例を示す。Y=Y,Y=Z,Z=3.のX,Y,Zは一見プログラムの中に宣言しているように見えるが、全て組込述語=/2の引数である。Prologにおいて=とは単一化を施すという意味である。変数の値の変化の例：?- X=Y, Y=Z, Z=3.X = 3,Y = 3,Z = 3true.?- W=5, W=3.false.論理変数は、格納場所ではなく、質問がなされる度に定義節を写して生成される一時的な論理域に存在するもので、プログラムの他の箇所からその値が参照されることはあり得ない。member(X,[X|_]).member(X,[_|R]) :-        member(X,R).第一節にXが二箇所、第二節にはXとRが二箇所現れている。それぞれの変数が最終的に同一のものに代入されることの宣言である。また上記の定義では第一節と第二節にXという論理変数が現れているが、この二つの論理変数名が同一であることには意味がない。第一節のXに値が代入されて解が得られた後、バックトラックされて第二節に移行したから値が解放されて、Xは代入されていないのではなくて、第二節のXは第一節のXとは元々無関係だから、代入されていないことになる。論理変数の名前が同一であることが意味を持つのは、質問されて、述語のひとつの定義節と融合された時、その融合された定義節側の頭部、本体の中に、同名の論理変数があるかないかだけである。質問した側の副目標の引数の論理変数がどのような表現になっているかについては没交渉である。融合の際の頭部の単一化でさえ、論理変数同士の単一化であっても、ソースプログラムで変数名が同じあるか否かは問われない。この点については、次の節の単一化を参照。ここで起こっている質問としての副目標(または目標)と定義節との融合は、述語定義のコードからは離れて、対応する節がその姿を写されて実行されるのであって、その実行と述語定義のコードは直接的な関係を持たない。したがって、この質問、導出過程で論理変数が束縛されても、述語定義の他の定義節の論理変数に影響を及ぼしようがないのである。
Prologの述語はPrologの基本データタイプである項で表現でき、述語自身の引数として別の述語を与える高階述語を作成できる。たとえば、引数で述語を与えそれを単純に実行させたい場合、ゴールとして変数をそのまま書けばいい。eval(P) :- P.またはeval(P) :- call(P).本来はcall/2が使われたが、その後、目標Pとだけ書くことでcall(P)と解釈されるようになった。述語名evalはLISPで同様の機能の関数の関数名としてこれが使われた伝統を引いてこの述語名が使わることがある。このように、任意の述語を実行時に作成して実行することができるため、リストのすべての要素に特定の述語を適用し結果のリストを返す  maplist/2  や、リストの要素を与えられた述語の結果で選択する  sublist/3  などの高階述語を容易に作成できる。同様の高階述語には  findall/3  、 setof/3  、 bagof/3  などがある。また、純粋なPrologのメタインタプリタは以下のように書ける。 clause(P, Q)  は頭部が P にユニフィケーション可能な節の本体部 Q を取得する述語である。execute(true) :- !. % trueならば成功execute((P,Q)) :- !, execute(P), execute(Q). % P1,... ,Pnの各要素を左から順に試みるexecute(P) :- clause(P, Q), execute(Q). % ゴールPの定義を取得し、本体Qを試みる純粋なPrologのメタインタプリタには組込述語がないとされるため、上記の通りであるが、実際のProlog処理系には組込述語が存在するため、上記定義では、第三節のclause(P, Q),の部分でPが組込述語の頭部になった時、この副目標が偽となっとしまうことがある。このclause(P, Q),を実行する前に、ユーザ定義述語か、組込述語かを検査し、別の節に分離する必要がある。組込述語についてはclause(P, Q),することなしに、単にP,すればよい。データとプログラムの形式が同じであることや、任意の演算子がユーザ定義可能なこと、Prolog自身が強力な構文解析機能を持つことなどもあり、このようなメタインタプリタをもとにPrologの一部機能を拡張した別言語のインタプリタを作成することは、比較的容易である。関数型言語で高階関数を代表するmap()はPrologでも高階述語を使って定義可能である。しかし、関数型では出力は返り値だけであったが、Prologでは引数のどれかである。入力も複数あり得る。しかもどの引数が入力であるか、あるいは出力であるかを示すモード宣言は、ほとんどの処理系で採用されていない。したがって、Prologでmap述語を構成するためには、対象となるリストの項はどの引数に当たるのかを陽に示さなくてはならない。以下に、map述語を二つ示す。map/4とmap/5である。map(_副目標,_要素,_対象リスト,_収集された副目標リスト) :-        findall(_収集解,(                    member(_要素,_対象リスト),                    _副目標),_収集された副目標リスト).map(_副目標,_要素,_対象リスト,_収集項,_収集項のリスト) :-        findall(_収集項,(                    member(_要素,_対象リスト),                    _副目標),_収集項のリスト).どちらも、対象リストの要素が反映して副目標の実行が変化して、それをリストに収集している。map/4は収集されるのが実行された_副目標 自体であるのに対して、map/5では実行された _副目標 自体ではなく、実行した時々に構成された _収集項 がリストに積み上がる。以下にこのmap述語使用例を示す。?- map(sub_atom(_文字列,S,Len,_,S),       [_文字列,S,Len], [[abcde,0,2],[efgh,2,1],[qazxyz,1,2]], L).L = [sub_atom(abcde,0,2,3,ab),sub_atom(efgh,2,1,1,g),sub_atom(qazxyz,1,2,3,az)]?- map(sub_atom(_文字列,S,Len,_,S),       [_文字列,S,Len], [[abcde,0,2],[efgh,2,1],[qazxyz,1,2]],S, L).L = [ab,g,az]となる。上記事例で map は、_副目標の引数 _文字列,S,Len,Y が map述語の中で巧みに結びつけられているが、やはり難解である。これではfindall/3を直接使って、?- findall(Y,(         member([_文字列,S,Len],[[abcde,0,2],[efgh,2,1],[qazxyz,1,2]]),         sub_atom(_文字列,S,Len,_,Y)),L).L = [ab,g,az]と記述してしまっても差がない。member/2が副目標として追加されただけの差である。一般に Prolog に於いて高階述語を使ったmap述語はあまり使用されないが、Prologにはこのように強力なメタ述語 findall や setof が既に存在している。しかも、関数型言語のように引数が事前評価されることはなく、引数に関数を置いて渡すこともできないため、map述語の効用は関数型言語のようには大きくない。
Prologは構文解析を行うのに向いたプログラミング言語である。元々、Prologは論理を利用した自然言語処理のために開発された。実際、文脈自由文法のトップダウン構文解析の動きはProlog自身の動きと同じである。
Prologには限定節文法（英: definite clause grammar）と呼ばれる特別な表記法が用意されている。文脈自由文法を拡張したもので、文法を記述する場合は :-/2 ではなく -->/2 を用いる。 head --> body.文法での非終端記号はPrologの項で、終端記号は非終端記号と区別するためリスト内の項で表現する。付加的な条件や動作を指定したい場合、文法の最後に任意のProlog述語を  { }  で囲んで記述する。限定節文法の例を以下に示す。この例では数式を解析し計算を行う。expression(E) --> term(X), [+], expression(Y), {E is X + Y}.expression(E) --> term(X), [-], expression(Y), {E is X - Y}.expression(E) --> term(E).term(T) --> num(X), [*], term(Y), {T is X * Y}.term(T) --> num(X), [/], term(Y), {T is X / Y}.term(T) --> num(T).num(N) --> [+], num(N).num(N) --> [-], num(X), {N is -X}.num(N) --> [N], {number(N), between(0, 9, N)}.これはバッカス・ナウア記法で書かれた以下の文法規則に計算の動作を付加したものと同じ意味を持つ。<expression> ::= <term> "+" <expression> | <term> "-" <expression> | <term><term> ::= <num> "*" <term> | <num> "/" <term> | <num><num> ::= "+" <num> | "-" <num> | 0..9実行結果は以下のようになる。?- expression(Z,[-, 2, +, 9, *, 2, +, 3, *, 5],[]).Z = 31このように直接計算を行うのではなく抽象構文木を作成するような文法規則を作成することもできる。構文木はPrologの項として素直に表現できるため、その後の機械語へのコンパイルや最適化などを行うことも可能である。次の、極めて簡単な日本語の解析例を見てみよう。先程の計算例では頭部の引数は変数であったが、文法的な解析結果を項として積み上げるために、ここでは変数でなく、直接ここに項の構造を記述してしまうことにする。文(文(_主部,_述部)) --> 主部(_主部), 述部(_述部).主部(主部(_名詞句)) --> 名詞句(_名詞句).名詞句(名詞句(_名詞,_後置詞)) --> 名詞(_名詞),後置詞(_後置詞).名詞(名詞(サザエ)) --> [サザエ].名詞(名詞(マスオ)) --> [マスオ].後置詞(後置詞(が)) --> [が].後置詞(後置詞(は)) --> [は].述部(述部(_動詞)) --> 動詞(_動詞).述部(述部(_形容詞)) --> 形容詞(_形容詞).動詞(動詞(泳ぐ)) --> [泳ぐ].形容詞(形容詞(美しい)) --> [美しい].形容詞(形容詞(速い)) --> [速い].注目するべきことは、本体が極めて簡素で読み易いことだ。ここでの例は厳密な文法であるとは言えないが、以下のような解析や文の生成が可能になる。?- 文(A,[サザエ,は,速い],B).A = 文(主部(名詞句(名詞(サザエ), 後置詞(は))), 述部(形容詞(速い))),B = [] ;false.?- 文(A,[サザエ,は,速い,マスオ,が,泳ぐ],B).A = 文(主部(名詞句(名詞(サザエ), 後置詞(は))), 述部(形容詞(速い))),B = [マスオ, が, 泳ぐ] ;false.?- 文(A,B,C).A = 文(主部(名詞句(名詞(サザエ), 後置詞(が))), 述部(動詞(泳ぐ))),B = [サザエ, が, 泳ぐ|C] ;A = 文(主部(名詞句(名詞(サザエ), 後置詞(が))), 述部(形容詞(美しい))),B = [サザエ, が, 美しい|C] ;A = 文(主部(名詞句(名詞(サザエ), 後置詞(が))), 述部(形容詞(速い))),B = [サザエ, が, 速い|C] ;A = 文(主部(名詞句(名詞(サザエ), 後置詞(は))), 述部(動詞(泳ぐ))),B = [サザエ, は, 泳ぐ|C] ;A = 文(主部(名詞句(名詞(サザエ), 後置詞(は))), 述部(形容詞(美しい))),B = [サザエ, は, 美しい|C] ;A = 文(主部(名詞句(名詞(サザエ), 後置詞(は))), 述部(形容詞(速い))),B = [サザエ, は, 速い|C] ;A = 文(主部(名詞句(名詞(マスオ), 後置詞(が))), 述部(動詞(泳ぐ))),B = [マスオ, が, 泳ぐ|C] ;A = 文(主部(名詞句(名詞(マスオ), 後置詞(が))), 述部(形容詞(美しい))),B = [マスオ, が, 美しい|C] ;A = 文(主部(名詞句(名詞(マスオ), 後置詞(が))), 述部(形容詞(速い))),B = [マスオ, が, 速い|C] ;A = 文(主部(名詞句(名詞(マスオ), 後置詞(は))), 述部(動詞(泳ぐ))),B = [マスオ, は, 泳ぐ|C] ;A = 文(主部(名詞句(名詞(マスオ), 後置詞(は))), 述部(形容詞(美しい))),B = [マスオ, は, 美しい|C] ;A = 文(主部(名詞句(名詞(マスオ), 後置詞(は))), 述部(形容詞(速い))),B = [マスオ, は, 速い|C].二番目の例は与えられた語リストの途中で文の解析が完了した場合は、質問の第三引数Cには残りの未解析部分のリストが返されることを示している。最後の例はBに変数を置き、可能な全ての文を生成させている。第一引数に積み上がった項が第二引数の語リストの「意味」であると考えられる。限定節文法の文法規則は、Prologの構文とは全く独立したもののように見えるが、実際にはProlog節を見やすくするための糖衣構文である。他のプログラミング言語でのマクロ展開のように、文法規則読み込み時にPrologの述語に変換される。変換規則は  expand_term/2  で定義されている。たとえば、 p(X,Y) --> q(X), r(X,Y), s(Y). の文法規則は p(X,Y,S0,S) :- q(X,S0,S1), r(X,Y,S1,S2), r(Y,S2,S). の節に変換され、付加された変数間で解析の情報が受け渡される。
"Hello World"は1970年代から「コンピュータはプログラム言語を使って、こんなにも簡単に動くものですよ」ということを感じさせる課題として好んで使われ、今日では教則本の冒頭にこれを置くことが半ば様式化してしまった。しかしPrologの述語は基本的に真偽値を問うものであって、入出力は副作用として疎ましい存在であり、冒頭にHello World述語を持ってくることには反対意見が強い。そういう事情を理解した上で、ここでは、この課題をPrologプログラミングの導入として利用してみよう。Prologは質問すると、処理系が答えを返す系である。"Hello World!"という表示は処理系が行うのだから、その前に質問がなくてはならない。質問を "hi" とする。hi :-        write('Hello World!\n').writeは引数が一個の組込述語である。引数の内容を表示する。world!の後の"\n"は改行することを意味する。実行例で見てみよう。Prologインタプリタは一般にプロンプトとして "?- "　が表示された後に、ユーザーが質問を入れる。?- hi.Hello World!述語名が hi であっても hi(a) :- や hi(1,2,3) :- の定義もあるかもしれない。Prologではこれらを区別するために、それぞれhi/0 hi/1 hi/3 のようにスラッシュの後に引数の数を書いて区別する。ここでは hi/0 である。質問 hi に対して、定義済みの hi が呼び出されて、その中でさらに write('Hello World!\n') が呼び出された。ここでは "Hello World!\n" は組込述語 write/1 の中に直接記述されたが、一般にはこのような原初的な情報はデータベース述語によって管理され、そこから引き出されて使われる。hi :-        answer(hi,S),        write(S).answer(hi,'Hello World!\n').S は先頭が英大文字だから論理変数である。answer/2はプログラマが定義したデータベース述語である。副目標answer(hi,S)は述語定義 answer/2 と融合され、各引数が単一化される。第一引数はatom同士でしかもhiで完全一致、第二引数はSと'Hello Wolrld!\n'が単一化されて、これは単一化のルールにより無条件に論理変数Sは'Hello World!\n'となる。そのS、乃ち'Hello World!\n'をwrite/1する。さらにこのプログラムに bye を追加してみよう。hi :-        answer(hi,S),        write(S).bye :-        answer(bye,S),        write(S),        halt.answer(hi,'Hello World!\n').answer(bye,'See you again!\n').述語 bye/0 の本体 (:- の右側) の最後にある halt は0引数の組込述語で処理系を終了させる。実行例?- hi.Hello World!?- bye.See you again!#Hello World からの導入はこんなところだろう。
よく知られる漫画作品・「サザエさん」の家系図(部分)をPrologで表現する。親子(波平,サザエ).親子(ふね,サザエ).親子(波平,カツオ).親子(ふね,カツオ).親子(波平,ワカメ).親子(ふね,ワカメ).親子(マスオ,タラオ).親子(サザエ,タラオ).夫婦(波平,ふね).夫婦(マスオ,サザエ).このように、本体がない、あるいは本体のtrueが省略された定義を、単位節と呼ぶ。親子、夫婦の両述語はともに第一引数または第二引数をキーとしてデータを参照することができる一種のデータベースと考えることができる。このような単位節データベースは全ての知識の基礎であって、必ずしもすぐにプログラムとして利用されなくても価値がある。ノートに書き付けるように身の回りの知識を定義していけばよい。備忘録としての単位節データベースへ実際に問いかける例を示そう。「サザエの親は誰か」という質問をする。?-  親子(_親,サザエ)._親 = 波平ここで一旦処理系は停止する。この解に満足な時はただ改行する。?- 親子(_親,サザエ)._親 = 波平 ;_親 = ふね.?-1) 波平に満足できない。2) 波平以外の解がほしい。そんな場合には停止中のカーソルにセミコロン(;)を入力すると、次の解を示してくる。_親 = ふね だ。他にも親はいないものかとさらにセミコロンを入力すると、もうこれ以上解はないと、この処理系では入力したセミコロンをピリオドに置き換えて表示して質問は終わりとなる。
前題の家系図が既に定義済みという前提で、祖父-孫 関係を定義してみよう。'祖父-孫'(波平,タラオ).祖父・孫の関係になれるのは波平とタラオだけである。'祖父-孫'という述語名はハイフンのような記号を含むアトムとなるため、シングルクォートで囲む必要がある例として示した。述語名はこれに限らず、 祖父孫 でも 祖父と孫 でも 祖父孫関係 あるいは単に 祖父 でも、特にこうしなくてはいけないというルールはない。処理系への知識の与え方は基本的に自由である。今度は、「孫」を定義する。上記の定義に倣うなら '祖父母-孫' 関係ということになるが、ここでは単に「孫」とする。孫(波平,タラオ).孫(ふね,タラオ).実行例を示そう。?- 孫(X,タラオ).X = 波平 ;X = ふね.?-一般に質問する時は、入力の負担を減らすために、最少の文字数となる XとかA,Bなど英大文字一文字を論理変数に置くことが多い。Xを使うことが多いのは、方程式の解となる変数をXとする習慣を引き継いだものと考えられる。もちろん家系図の時のように ?- 孫(_祖父母,タラオ). と質問しても構わないし、常にそういう習慣があるならそれが望ましい。孫を具体的な人と人の関係として定義して見たが、以下のような定義も可能である。このような定義をルールという。孫(_祖父または祖母,_孫) :-        親子(_祖父または祖母,A),        親子(A,_孫).Aは孫から見ると親であるが、祖父または祖母から見ると親ではないので、_親 とはせずに、英大文字の A で抽象化した。本体の二つの副目標である親子/2のそれぞれの引数に共通のAが存在することが重要である。孫の親と祖父または祖母の子が同一人物のAであることを示している。だから% これでは具合が悪い!孫(_祖父または祖母,_孫) :-        親子(_祖父または祖母,_祖父または祖母の子),        親子(_孫の親,_孫).行の先頭に % がある一行目は「註釈行」でありプログラム実行の対象とはならない。述語形式とは限らない自由な文を書くこともできる。_祖父または祖母の子 と _孫の親 とは単一化できていないため、変数名から同一が類推できるとしても、処理系は A の時のように同一のものと扱わない。それならば、_祖父または祖母の子 と _孫の親 を単一化させてしまえばよい。それを実行したのが下のコードである。孫(_祖父または祖母,_孫) :-        親子(_祖父または祖母,_祖父または祖母の子),        親子(_孫の親,_孫),        _祖父または祖母の子 = _孫の親.単一化述語である =/2 で二つの変数が実は同じものであるが明確になる。二つの視点からそれぞれ別の変数シンボルになってしまうことは屡々ある。そのような場合は単一化で解決する。家系図の中で、このような二つの親子関係が連接して、孫関係を充たすのは、?- 孫(X,Y).X = 波平,Y = タラオ;X = ふね,Y = タラオ .?-である。
与えられたリスト要素を加算する。再帰的な定義である。リスト要素の加算([],0).リスト要素の加算([N|R],S) :-        リスト要素の加算(R,S1),        S is S1 + N.1要素少ないRの加算計がS1ならばSはS1にNを加えたものだ、という宣言である。実行例: このように定義しておけば、以下の質問は?- リスト要素の加算([1,2,3,4,5,6,7,8,9,10],X).X = 55となる。加算は以下のように、二つの述語に分離して、引数をひとつ増やした述語を作ると累算部分がはっきりしてわかりやすい。加算を担うのは後の方、引数が3個ある方の述語だ。述語名は引数の数が違うから同じ リスト要素の加算 で構わない。リストから数を取り出すと、この第二引数に加算していく。% 1リスト要素の加算(L,S) :-        リスト要素の加算(L,0,S).% 2リスト要素の加算([],S,S).リスト要素の加算([N|R],S1,S) :-        S2 is S1 + N,        リスト要素の加算(R,S2,S).リスト要素の加算/3 の第一節の %1 の下のリスト要素の加算([],S,S).というところがProlog独特のテクニックである。第一引数のリストが空になったとき、第二引数に累計が存在するのだが、最初の %1 述語リスト要素の加算/2の副目標        リスト要素の加算(L,0,S).の第二引数では初期値0と値を決めて引数に渡しているため、この第二引数から値を受け取ることは不可能である。そこで、第三引数として一つ余分な引数を設けて、そちらを変数にして置けば、最終的にその引数が単一化されることによって値を受け取ることができるという訳だ。その第二引数と第三引数の単一化を行っているのが、%2 の リスト要素の加算/3の第一節のリスト要素の加算([],S,S).である。この述語の第二節第三引数がともにSで同一であることも重要である。リスト要素の加算([N|R],S1,S) :-        S2 is S1 + N,        リスト要素の加算(R,S2,S).この部分が束縛されないまま、単に同一の変数であることが示されている。質問の第一引数が[]になれば、この述語の第二節が融合されて、質問側に用意されている第二引数の加算計がこれに単一化される。リスト要素の加算([],S,S).これまで第三引数は全て同一ということになっているため、この第三引数経由で加算値が質問の第二引数に返される。
単位節(本体のない事実上のデータベース定義)要素の加算。実例として次の単位節データベースを考える。年齢(山田,35).年齢(大島,20).年齢(清川,28).ここでは年齢の合計を計算する。簡単なデータベースの参照は、?- 年齢(A,B).A = 山田,B = 35;A = 大島,B = 20;A = 清川,B = 28 .?-Prologの述語の中のそれぞれの節に現れる要素は他の節から完全に独立である。すなわち一つの節の中の値は別の節からは参照できない。山田を得たとき、大島を得たとき、清川を得たときはそれぞれ独立している。以前の変数の束縛は解かれてしまっている。大島の20を得たときには、山田の35の情報は失ってしまっているということになる。これでは加算のような集約問題を解決できない。このことを可能にするために、メタ述語 findall/3 が存在する。findall/3 はSQLのselect文に似た述語であり、述語を実行した際に任意の値をリストに集めることができる。年齢(山田,35).年齢(大島,20).年齢(清川,28).年齢合計(X) :-        findall(N,年齢(_,N),L),        リスト要素の加算(L,X).本来、情報の連関のない述語 年齢/2 のそれぞれの節を、連関を持つデータ構造であるリストに取り込むことによって、集約を可能とする。実行例:?- 年齢合計(X).X = 83理解を深めるために、findall以下を直接質問として呼び出してみよう。?- findall(N,年齢(_,N),L),   リスト要素の加算(L,X).L = [35,20,28],X = 83となる。findallは強力な述語であるが、対象となる定義節数が極めて多い場合、例えば、1000万節を越えるような場合、スタックオーバーフロー等のエラーが発生する危険が生じる。内部メモリにリストとして情報の連鎖を生成するのだから、やむを得ないことではあるが、注意が必要である。
算術平均ともいい、一般に平均値といった場合これを指す。標本はリストとして保持しているとする。基本的には加算と同じだが、同時にリストの標本数も数える。第二、第三引数にそれぞれ初期値 0 を置き、これに標本数と値を加算していく。相加平均(_標本リスト,_相加平均) :-        相加平均(_標本リスト,0,0,_相加平均).相加平均([],_標本数,_累計,_相加平均) :-        _標本数 > 0,        _相加平均 is _累計 / _標本数.相加平均([_値|R],_標本数累計_1,_累計_1,_相加平均) :-        _標本数累計_2 is _標本数累計_1 + 1,        _累計_2 is _累計_1 + _値,        相加平均(R,_標本数累計_2,_累計_2,_相加平均).この述語では要素数や累計を求められていないため、これを入手するための変数は用意されていない。その代わりにリストが空の時に相加平均が計算されて単一化される。これが平均値の述語定義だが、多くの場合ここまでプログラマが定義する必要はない。下記のように、組込述語を含めて、定義済みの述語を組み合わせて相加平均は定義される。上記の要素数のカウント部分は独立して length という組込述語となっている。したがって、相加平均の定義は相加平均(_標本リスト,_相加平均) :-        length(_標本リスト,_標本数),        リスト要素の加算(_標本リスト,_合計),        _相加平均 is _合計 / _標本数.で構わない。
相加平均が使われる定義の一つ標準偏差の定義である。標準偏差本体の定義の前にこの相加平均の計算が完了している必要がある。標準偏差(L,V) :-        length(L,N),        相加平均(L,M),        標準偏差(L,N,M,0.0,V).標準偏差([],N,M,S,V) :-        V is sqrt(S / (N - 1)),!.標準偏差([A|R],N,M,S,V) :-        S1 is (A - M) ^ 2,        S2 is S + S1,        標準偏差(R,N,M,S2,V).is/2評価の中に現れる関数sqrt()で平方根を求める。
リストの最大値を求める。併せて、カットの典型的な用法について説明する。最初に初期値を設定して、それと再帰的に比較する。 初期値はリストの中の要素であれば、何でも構わないのだが、ここでは第一要素を使う。最大値(_標本リスト,_最大値) :-        _標本リスト = [_第一要素|R],        最大値(R,_第一要素,_最大値).最大値([],_最大値,_最大値).最大値([_要素|R],_最大値_1,_最大値) :-        _要素 > _最大値_1,!,           % ! の位置に注意。        最大値(R,_要素,_最大値).最大値([_要素|R],_最大値_1,_最大値) :-        最大値(R,_最大値_1,_最大値)._要素がこれまでの最大値を超えない時は最後の節が選択される。超えた場合は第二節の「_要素 > _最大値_1,!」のカットが働き最後の節が選択されることはなくなる。引数が3の最大値の第二節に「!」がある。これがないと、最大値(_標本リスト,_最大値) :-        _標本リスト = [_第一要素|R],        最大値(R,_第一要素,_最大値).最大値([],_最大値,_最大値).最大値([_要素|R],_最大値_1,_最大値) :-        _要素 > _最大値_1,        最大値(R,_要素,_最大値).最大値([_要素|R],_最大値_1,_最大値) :-        最大値(R,_最大値_1,_最大値).?- 最大値([2,4,6,8,3],X).X = 8;X = 6;X = 8;X = 4;X = 8;X = 6;X = 8;X = 3;X = 2;false.?-というようなことが起こりうる。バックトラックして来たときにそれまでで最大としたものを、「;」の入力で「それではない」と否定されて、撤回してしまう。せっかく見つけ出したそれまでの最大値であるべきものがこれまでの最大値として使われないためである。「!」を入れることが有効な場所を、最初の定義も含めて示す。%%% 案1 %%%最大値(_標本リスト,_最大値) :-        _標本リスト = [_第一要素|R],        最大値(R,_第一要素,_最大値).最大値([],_最大値,_最大値).最大値([_要素|R],_最大値_1,_最大値) :-        _要素 > _最大値_1,!,             % ! の位置に注意。        最大値(R,_要素,_最大値).最大値([_要素|R],_最大値_1,_最大値) :-        最大値(R,_最大値_1,_最大値).%%% 案2 %%%最大値(_標本リスト,_最大値) :-        _標本リスト = [_第一要素|R],        最大値(R,_第一要素,_最大値).最大値([],_最大値,_最大値).最大値([_要素|R],_最大値_1,_最大値) :-        _要素 > _最大値_1,        最大値(R,_要素,_最大値),!.       % この節の末尾に ! がくる。最大値([_要素|R],_最大値_1,_最大値) :-        最大値(R,_最大値_1,_最大値).%%% 案3 %%%最大値(_標本リスト,_最大値) :-        _標本リスト = [_第一要素|R],        最大値(R,_第一要素,_最大値),!.   % ここに ! を打つ。非決定性述語 最大値/3 を事実上決定性述語とすることができる。最大値([],_最大値,_最大値).最大値([_要素|R],_最大値_1,_最大値) :-        _要素 > _最大値_1,        最大値(R,_要素,_最大値).最大値([_要素|R],_最大値_1,_最大値) :-        最大値(R,_最大値_1,_最大値).などが考えられる。ただし、案3は　最初の 最大値 で質問した場合は ! が有効になるが、最大値/3 の方で質問した場合は、「!」はないので、有効にならない。最大値/2の方で質問するように注意する必要がある。最大値([],_最大値,_最大値).の本体に必ずしも「!」が存在しない理由は、第一引数が [] で呼ばれた場合、さらに他の節が選択されることはこの定義の場合はあり得ないからだ。最初の最大値の定義の、最大値(_標本リスト,_最大値) :-        _標本リスト = [_第一要素|R],        最大値(R,_第一要素,_最大値).最大値([],_最大値,_最大値).最大値([_要素|R],_最大値_1,_最大値) :-        _要素 > _最大値_1,         % 今度は ! がない。        最大値(R,_要素,_最大値).最大値([_要素|R],_最大値_1,_最大値) :-        _要素 =< _最大値_1,        最大値(R,_最大値_1,_最大値).最後の節の本体に「_要素 =< _最大値」を加えれば「!」を排除することができる。最後に、最も宣言的な最大値の定義を示す。「選択した値以外の全ての要素が選択した値以下である時、選択した値が最大値である」がその意味である。最大値(_標本リスト,_最大値) :-        _選択した値 = _最大値,        select(_選択した値,_標本リスト,_選択した値を除くリスト),        forall(member(_要素,_選択した値を除くリスト),_要素 =< _選択した値),!.select/3とforall/2は共に組込述語となっている。select/3は第二引数のリストの先頭から要素を非決定性に取り出し、第一引数に単一化すると共に第三引数にその要素を除いたリストを単一化する。forall/2は 第一引数の評価を真とするものに全てに対して、第二引数の評価は真となる、というものである。