7

私は、Prologの実装が教科書の実行モデルに従って動作しない理由を理解しようとしています。たとえば、SterlingandShapiroの「TheArtofProlog」(第6章「PureProlog」、セクション6.1、 「プロローグの実行モデル」)。

私が参照している実行モデルは次のとおりです(Sterling&Shapiroの93ページ)。

入力:目標GとプログラムP

出力: Pの論理的帰結である、またはそれ以外の場合はないGのインスタンス

アルゴリズム:

Initialize resolvent to the goal G
while resolvent not empty:
    choose goal A from resolvent
    choose renamed clause A' <- B_1, ..., B_n from P
            such that A, A' unify with mgu θ
        (if no such goal and clause exist, exit the "while" loop)
    replace A by B_1, ..., B_n in resolvent
    apply θ to resolvent and to G
If resolvent empty, then output G, else output NO

さらに(同じ本の120ページ)、Prologはchoose goal A左から右の順序で目標()を選択choose renamed clause ...し、プログラムに表示される順序で句()を検索します。

not以下のプログラムには、 (プログラムで呼び出される)の定義nと1つの事実があります。

n(X) :- X, !, fail.
n(X).

f(a).

私が証明しようとするn(n(f(X)))と、それは成功します(2つの教科書とSWI Prolog、GNU Prolog、Yapによると)。しかし、これは少し奇妙ではありませんか?いくつかの本が公開しているその実行モデルによると、これは私が起こると予想されることです(とにかく競合がないので、物事を単純に保つために変数の名前変更をスキップします):

  • レゾルベント:n(n(f(Z)))

  • 統合Xは最初の節で。と一致しn(f(Z))、ゴールをその節の末尾に置き換えます。

  • レゾルベント:n(f(Z)), !, fail

  • 統一Xは、最初の節で再び一致f(Z)し、レゾルベントの最初の目標を節の末尾に置き換えます

  • レゾルベント:f(Z), !, fail, !, fail

  • 統一マッチf(Z)->成功!これで、これはレゾルベントから削除されます。

  • レゾルベント:!, fail, !, fail

そして「 」は成功しない!, fail, !, failはずです!カット後、失敗があります。物語の終わり。(そして実際、クエリとして入力すると、私がアクセスできるすべてのPrologシステムで失敗します)。!,fail,!,fail

それで、教科書の実行モデルは、Prologが正確に使用しているものではないと推測できますか?

編集:最初の句を変更して、n(X) :- call(X), !, fail試したすべてのプロローグに違いがないようにします。

4

6 に答える 6

6

n/1 に !/0 が含まれているため、プログラムは純粋な Prolog プログラムではありません。より簡単な質問を自問するかもしれません: あなたの定義では、プログラムに事実 n(X) が明らかにあるのに、クエリ?- n(f(X)). が失敗するのはなぜですか。つまり、n(X) はすべてのX に対して真であり、したがって特にf(X)も?これは、!/0 の使用により、プログラムの句を分離して考えることができなくなり、純粋な Prolog の実行モデルを使用できないためです。このような不純な述語のより現代的で純粋な代替手段は、多くの場合、dif/2 などの制約です。これにより、変数を項と区別できるように制約できます。

于 2012-07-23T14:28:34.380 に答える
6

以下のキャプションは、この特定のアルゴリズムが何であるかを示しています。

図 4.2論理プログラムの抽象インタープリター

また、その説明は次のとおりです。

出力: Pの論理的帰結であるGのインスタンス、またはそうでない場合のインスタンス。

つまり、4.2 のアルゴリズムは、論理プログラムの論理結果を計算する方法のみを示しています。Prologが実際にどのように機能するかについてのアイデアを提供するだけです。特に を説明することはできません!。また、4.2 のアルゴリズムは 1 つの解 (「結果」) がどのように見つかるかを説明することしかできませんが、Prolog は時系列バックトラッキングと呼ばれる体系的な方法でそれらすべてを見つけようとします。このカットは、このアルゴリズムのレベルでは説明できない非常に特殊な方法で時系列のバックトラックを妨害します。

あなたが書いた:

さらに (同書の 120 ページ)、Prolog は左から右の順序で目標を選択し、プログラムに表示される順序で (choose goal A)節を検索します。(choose renamed clause ...)

これは、120 ページで読むことができる 1 つの重要なポイントを見逃しています。

Prolog の実行メカニズムは、抽象インタプリタから左端のゴールを選択することによって取得されます...そして、非決定論的な節の選択を単一化可能な節の順次検索とバックトラックによって置き換えます。

したがって、物事をより複雑にするのは、この小さな追加の「および後戻り」です。これは抽象アルゴリズムでは見られません。

バックトラッキングがアルゴリズムで明示的に処理されないことを示す小さな例を次に示します。

p :-
 q(X),
 r(X).

q(1).
q(2).

r(2).

pwhich に書き換えることから始めq(X), r(X)ます (続行する方法は他にありません)。

次に、q(X)が選択され、θ = { X= 1} となります。したがってr(1)、解決策として持っています。しかし、今は一致する句がないので、「while ループを終了」してnoと答えます。

しかし、待ってください、解決策があります!では、どうやってそれを得るのですか?が選択された場合q(X)、θ の別のオプション、つまり θ = { X= 2} もありました。アルゴリズム自体は、この操作を実行するメカニズムについて明示的ではありません。それは、「どこでも正しい選択をすれば、答えが見つかるだろう」と言っているだけです。したがって、その抽象的なアルゴリズムから実際のアルゴリズムを取得するには、これを行うための何らかのメカニズムが必要です。

于 2012-07-23T22:26:20.380 に答える
4

最後のステップに到達したら:

  • 解決策: !, 失敗, !, 失敗

ここでのカット!は「すべてを消す」という意味です。したがって、リゾルベントは空になります。(これはもちろん偽物ですが、十分に近いです)。ここではカットはまったく意味がありません。最初failは決定をひっくり返すことを言い、2番目failはそれをひっくり返すことを言います。今、リゾルベントは空です - 決定は「YES」であり、2回裏返されたままです。(これも偽物です...「反転」はバックトラッキングがある場合にのみ意味があります)。

!もちろん、達成すべき目標の 1 つだけではないため、リゾルベントの目標のリストにカットを配置することはできません。これには操作上の意味があり、通常は「他の選択肢を試すのをやめる」と言いますが、このインタプリタはどの選択肢も追跡しません(すべての選択肢を「あたかも」一度に行うかのように)。は達成すべき目標だけではありません。fail

したがって、教科書の実行モデルは、正確には Prolog が使用するものではないと推測できますか?

はい、もちろん、実際のプロローグには、あなたが言及した抽象的なインタープリターcutとは異なります。failそのインタープリターには明示的なバックトラッキングがなく、代わりに魔法によって複数の成功があります (その選択は、あたかもすべての選択が一度に並行して行われるかのように、本質的に非決定論的です。実際の Prologs は、明示的なバックトラッキングを使用した順次実行によってのみそれをエミュレートします。参照 - それ以外の場合は意味がありません)。cut

于 2012-07-23T20:44:49.767 に答える
2

ほぼ正解だったと思います。問題はここにあります:

RESOLVENT: !, fail, !, fail.

最初 !および失敗は、最初の句が一致した2回目からのものです。残りの2つは初回から。

RESOLVENT: ![2], fail[2], ![1], fail[1].

切断と失敗は、処理中の句に影響を与えます。それを「呼び出した」句には影響しません。これらの注釈を使用して手順をもう一度実行すると、正しい結果が得られます。

![2], fail[2]nバックトラックせずに失敗するように 2 番目の呼び出しを行います。しかし、もう一方の呼び出し (最初の呼び出し) はバックトラックできます。

RESOLVENT: n(_)

そして結果は「はい」です。

これは、Prolog がスタック規則を使用してバックトラッキングに関する情報を保持していることを示しています。Prolog 実装のモデルとして使用される仮想マシンに興味があるかもしれません。あなたが言及した実行モデルよりもかなり複雑ですが、Prolog を VM に変換すると、Prolog がどのように機能するかをより正確に理解できるようになります。これがウォーレン抽象機械 (WAM) です。Hasan Aït-Kaci によるチュートリアルは、あなたが見つけることができる最良の説明です (そして、私の記憶が正しければ、元の WAM の説明にはなかったカットについて説明しています)。理論的なテキストを抽象化することに慣れていない場合は、最初に Peter van Roy によるテキストを読んでみてください: " 1983-1993: 順次 Prolog 実装の不思議な年この記事は明確であり、基本的に Prolog 実装の歴史をたどっていますが、WAM に特に注意を払っています。ただし、カットがどのように実装されているかは示されていません。 Hasan のチュートリアルを開き、カットを実装するセクションを読んでください。

于 2012-07-25T11:59:45.343 に答える
1

プログラムが純粋なプロローグではないという点で mat は正しいですが (この章のタイトルが純粋なプロローグであるため、これは関連しています)、カットを使用するだけでなく、他の述語を処理する述語を作成するためです (純粋なプロローグは一次論理のサブセット) これは主な問題ではありません。あなたはバックトラックを逃しているだけです

確かにカットがありますが、ゴール n(f(X)) が成功するまで、これには到達しません。ただし、ご存知のように、これは失敗するため、prolog はバックトラックして 2 番目の句に一致します。

それが 6.1 で説明されているモデルとどのように矛盾するのかわかりません (また、失敗した後も実行が継続され、カットが他のソリューションを削除できるモデルを他の本が説明しているとは信じがたいでしょう)。いずれにせよ、「Prolog の実装は教科書の実行モデルに従って動作しない」という結論にジャンプすることは、「コンパイラにバグがある」ことに非常に似ていることがわかります。特に、「反例」は次のように動作するためです。それは (not(not(true)) であるべきです)

于 2012-07-23T15:06:00.150 に答える
1

テストの目標には、追加レベルのネストがあります。

n(n(f(X))

それ以外の:

n(f(X))

実際、それを試してみると、期待どおりに動作します。

$ prolog
GNU Prolog 1.3.0
By Daniel Diaz
Copyright (C) 1999-2007 Daniel Diaz
| ?- [user].
compiling user for byte code...
n(X) :- call(X), !, fail.
n(_X).
f(a).

user compiled, 4 lines read - 484 bytes written, 30441 ms

yes
| ?- f(a).

yes
| ?- n(f(a)).

no
| ?- n(f(42)).

yes
| ?- n(n(f(X))).

yes
| ?- n(f(X)).

no
| ?- halt.

したがって、Prolog についてのあなたの理解は正しいのですが、テスト ケースはそうではありませんでした。

更新しました

否定の否定の効果を示す:

$ prolog
GNU Prolog 1.3.0
By Daniel Diaz
Copyright (C) 1999-2007 Daniel Diaz
| ?- [user].                                                            
compiling user for byte code...
n(X) :- format( "Resolving n/1 with ~q\n", [X] ), call(X), !, fail.
n(_X).
f(a) :- format( "Resolving f(a)\n", [] ).

user compiled, 4 lines read - 2504 bytes written, 42137 ms

(4 ms) yes
| ?- n(f(a)).
Resolving n/1 with f(a)
Resolving f(a)

no
| ?- n(n(f(a))).
Resolving n/1 with n(f(a))
Resolving n/1 with f(a)
Resolving f(a)

yes
| ?- n(n(n(f(a)))).
Resolving n/1 with n(n(f(a)))
Resolving n/1 with n(f(a))
Resolving n/1 with f(a)
Resolving f(a)

no
| ?- n(n(n(n(f(a))))).
Resolving n/1 with n(n(n(f(a))))
Resolving n/1 with n(n(f(a)))
Resolving n/1 with n(f(a))
Resolving n/1 with f(a)
Resolving f(a)

yes
| ?- halt.
于 2012-07-23T14:12:17.837 に答える