40

きれいな方法で項の等号と不等号を区別する純粋な Prolog プログラムは、実行の非効率性に悩まされます。関連性のすべての用語が根拠がある場合でも。

SO の最近の例はthis answerです。この定義では、すべての答えとすべての失敗が正しいです。検討:

?- Es = [E1,E2], occurrences(E, Es, Fs).
Es = Fs, Fs = [E, E],
E1 = E2, E2 = E ;
Es = [E, E2],
E1 = E,
Fs = [E],
dif(E, E2) ;
Es = [E1, E],
E2 = E,
Fs = [E],
dif(E, E1) ;
Es = [E1, E2],
Fs = [],
dif(E, E1),
dif(E, E2).

このプログラムは宣言的な観点からは完璧ですが、B、SICStus、SWI、YAP などの現在のシステムで直接実行するのは不必要に非効率的です。次の目標では、リストの要素に対して選択ポイントが開いたままになります。

?- オカレンス(a,[a,a,a,a,a],M)。
M = [a、a、a、a、a] ;
偽

これは、次のように s の十分に大きなリストを使用することで確認できaます。Iリストを表示できるように を調整する必要がある場合があります。SWI では、これは次のことを意味します。

1mo はI、次のようなグローバル スタックのリソース エラーを防ぐのに十分小さい必要があります。

?- 24=I,N は 2^I,length(L,N), maplist(=(a),L) です。
エラー: グローバル スタックが不足しています

2do はI、ローカル スタックのリソース エラーを引き起こすのに十分な大きさである必要があります。

?- 22=I,N は 2^I,length(L,N), maplist(=(a),L), ( Length=ok ; 発生数(a,L,M) ).
私= 22、
N = 4194304、
L = [a, a, a, a, a, a, a, a, a|...],
長さ = OK ;
エラー: ローカル スタックが不足しています

この問題を克服し、優れた宣言型のプロパティを維持するには、比較述語が必要です。


この比較述語はどのように定義する必要がありますか?

考えられる定義は次のとおりです。

equality_reified(X, X, true).
equality_reified(X, Y, false) :-
   差分 (X, Y)。

編集:おそらく、ISO組み込みと同様に引数の順序を逆にする必要がありますcompare/3(ドラフトのみへのリンクリンク)。

それを効率的に実装すると、最初に高速で確定的なケースが処理されます。

equality_reified(X, Y, R) :- X == Y, !, R = true.
equality_reified(X, Y, R) :- ?=(X, Y), !, R = false. % 構文的に異なる
equality_reified(X, Y, R) :- X \= Y, !, R = false. % 意味的に異なる
equality_reified(X, X, true).
equality_reified(X, Y, false) :-
   差分 (X, Y)。

X \= Y編集:制約がある場合に適切なガードであるかどうかは明確ではありません。制約なし、?=(X, Y)またはX \= Y同じです。


@ user1638891 で提案されているように、このようなプリミティブの使用例を次に示します。マットによる元のコードは次のとおりです。

occurrences_mats(_, [], []).
occurrences_mats(X, [X|Ls], [X|Rest]) :-
   occurrences_mats(X, Ls, Rest).
occurrences_mats(X, [L|Ls], Rest) :-
   dif(X, L),
   occurrences_mats(X, Ls, Rest).

次のように書き換えることができます。

occurrences(_, [], []).
occurrences(E, [X|Xs], Ys0) :-
   reified_equality(Bool, E, X),
   ( Bool == true -> Ys0 = [X|Ys] ; Ys0 = Ys ),
   % ( Bool = true, Ys0 = [X|Ys] ; Bool = true, Ys0 = Ys ),
   occurrences(E, Xs, Ys).

reified_equality(R, X, Y) :- X == Y, !, R = true.
reified_equality(R, X, Y) :- ?=(X, Y), !, R = false.
reified_equality(true, X, X).
reified_equality(false, X, Y) :-
   dif(X, Y).

SWI の 2 番目の引数のインデックス作成は、 のようなクエリを入力した後にのみ有効になることに注意してくださいoccurrences(_,[],_)(;)/2また、SWI は、選言にインデックスを付けないため、本質的に非単調な if-then-else を必要とします。SICStus はそうしますが、最初の引数のインデックス付けしかありません。したがって、1 つの選択ポイントが開いたままになります (最後に が付き[]ます)。

4

6 に答える 6

9

一つには、名前は。のように、より宣言的なものにする必要がありますequality_truth/2

于 2012-12-02T01:06:32.287 に答える
8

次のコードは、 AUBUC のProlog ユニオンで @falseによって実装されているif_/3and (=)/3(aka ) に基づいています。equal_truth/3

=(X, Y, R) :- X == Y,    !, R = true.
=(X, Y, R) :- ?=(X, Y),  !, R = false. % syntactically different
=(X, Y, R) :- X \= Y,    !, R = false. % semantically different
=(X, Y, R) :- R == true, !, X = Y.
=(X, X, true).
=(X, Y, false) :-
   dif(X, Y).

if_(C_1, Then_0, Else_0) :-
   call(C_1, Truth),
   functor(Truth,_,0),  % safety check
   ( Truth == true -> Then_0 ; Truth == false, Else_0 ).

と比較するoccurrences/3と、補助関数occurrences_aux/3は、リストを最初の引数として渡す別の引数順序を使用しEsます。これにより、最初の引数のインデックス付けが可能になります。

occurrences_aux([], _, []).
occurrences_aux([X|Xs], E, Ys0) :-
   if_(E = X, Ys0 = [X|Ys], Ys0 = Ys),
   occurrences_aux(Xs, E, Ys).

@migfilg が指摘したように、論理的に false であるため、ゴールは失敗する Fs=[1,2], occurrences_aux(Es,E,Fs) はずです: のすべての要素が に等しいとoccurrences_aux(_,E,Fs)述べています。ただし、このような場合、単独では終了しません。FsEoccurrences_aux/3

補助述語を使用して、allEqual_to__lazy/2終了動作を改善できます。

allEqual_to__lazy(Xs,E) :-
   freeze(Xs, allEqual_to__lazy_aux(Xs,E)).

allEqual_to__lazy_aux([],_).
allEqual_to__lazy_aux([E|Es],E) :-
   allEqual_to__lazy(Es,E).

occurrences/3すべての補助述語を配置したら、次を定義しましょう。

occurrences(E,Es,Fs) :-
   allEqual_to__lazy(Fs,E),    % enforce redundant equality constraint lazily
   occurrences_aux(Es,E,Fs).   % flip args to enable first argument indexing

いくつかのクエリを考えてみましょう:

?- occurrences(E,Es,Fs).       % first, the most general query
Es = Fs, Fs = []        ;
Es = Fs, Fs = [E]       ;
Es = Fs, Fs = [E,E]     ;
Es = Fs, Fs = [E,E,E]   ;
Es = Fs, Fs = [E,E,E,E] ...    % will never terminate universally, but ...
                               % that's ok: solution set size is infinite

?- Fs = [1,2], occurrences(E,Es,Fs).
false.                         % terminates thanks to allEqual_to__lazy/2

?- occurrences(E,[1,2,3,1,2,3,1],Fs).
Fs = [1,1,1],     E=1                     ;
Fs = [2,2],                 E=2           ;
Fs = [3,3],                           E=3 ;
Fs = [],      dif(E,1), dif(E,2), dif(E,3).

?- occurrences(1,[1,2,3,1,2,3,1],Fs).
Fs = [1,1,1].                  % succeeds deterministically

?- Es = [E1,E2], occurrences(E,Es,Fs).
Es = [E,  E], Fs = [E,E],     E1=E ,     E2=E  ;
Es = [E, E2], Fs = [E],       E1=E , dif(E2,E) ;
Es = [E1, E], Fs = [E],   dif(E1,E),     E2=E  ;
Es = [E1,E2], Fs = [],    dif(E1,E), dif(E2,E).

?- occurrences(1,[E1,1,2,1,E2],Fs).
    E1=1 ,     E2=1 , Fs = [1,1,1,1] ;
    E1=1 , dif(E2,1), Fs = [1,1,1]   ;
dif(E1,1),     E2=1 , Fs = [1,1,1]   ;
dif(E1,1), dif(E2,1), Fs = [1,1].

編集 2015-04-27

occurrences/3特定のケースでユニバーサルが終了するかどうかをテストするためのクエリをいくつか追加します。

?-           occurrences(1,L,[1,2]).
false. 
?- L = [_|_],occurrences(1,L,[1,2]).
false.
?- L = [X|X],occurrences(1,L,[1,2]).
false.
?- L = [L|L],occurrences(1,L,[1,2]).
false.
于 2015-04-17T20:22:16.313 に答える
4

のさらに短い論理的に純粋な実装を次に示しoccurrences/3ます。

tfilter/3、具体化された用語 equality predicate (=)/3、およびコルーチン(この質問に対する以前の回答allEqual_to__lazy/2で定義)に基づいて構築します。

occurrences(E,Xs,Es) :-
   allEqual_to__lazy(Es,E),
   tfilter(=(E),Xs,Es).

終わり!比較を容易にするために、以前の回答で使用したのとまったく同じクエリを再実行します。

?- Fs = [1,2], occurrences(E,Es,Fs).
false.

?- occurrences(E,[1,2,3,1,2,3,1],Fs).
Fs = [1,1,1],     E=1                     ;
Fs = [2,2],                 E=2           ;
Fs = [3,3],                           E=3 ;
Fs = [],      dif(E,1), dif(E,2), dif(E,3).

?- occurrences(1,[1,2,3,1,2,3,1],Fs).
Fs = [1,1,1].

?- Es = [E1,E2], occurrences(E,Es,Fs).
Es = [E, E ], Fs = [E,E],     E1=E ,     E2=E  ;
Es = [E, E2], Fs = [E],       E1=E , dif(E2,E) ;
Es = [E1,E ], Fs = [E],   dif(E1,E),     E2=E  ;
Es = [E1,E2], Fs = [],    dif(E1,E), dif(E2,E).

?- occurrences(1,[E1,1,2,1,E2],Fs).
    E1=1 ,     E2=1 , Fs = [1,1,1,1] ;
    E1=1 , dif(E2,1), Fs = [1,1,1]   ;
dif(E1,1),     E2=1 , Fs = [1,1,1]   ;
dif(E1,1), dif(E2,1), Fs = [1,1].

?- occurrences(1,L,[1,2]).
false.

?- L = [_|_],occurrences(1,L,[1,2]).
false.

?- L = [X|X],occurrences(1,L,[1,2]).
false.

?- L = [L|L],occurrences(1,L,[1,2]).
false.

最後に、最も一般的なクエリ:

?- occurrences(E,Es,Fs).
Es = Fs, Fs = []      ;
Es = Fs, Fs = [E]     ;
Es = Fs, Fs = [E,E]   ;
Es = Fs, Fs = [E,E,E] % ... and so on ad infinitum ...

同じ答えが得られます。

于 2015-06-16T01:31:53.417 に答える
2

以下の実装はoccurrences/3、私の以前の回答に基づいています。つまり、いくつかの選択ポイントを回避するために、第 1 引数の句索引付けメカニズムから利益を得て、提起されたすべての問題に対処しています。

さらに、質問で言及されているものを含め、これまでに提出されたすべての実装の問題、つまり、クエリの最初の2つの引数が無料で、3番目の引数が異なるグラウンド要素を持つリストである場合、それらはすべて無限ループに入るという問題に対処します。もちろん、正しい動作は失敗することです。

比較述語の使用

未使用の選択ポイントを回避し、実装の宣言性をある程度維持するために、質問で提案されているような比較述語は必要ないと思いますが、これは好みや傾向の問題である可能性があることに同意します。

実装

3 つの排他的なケースがこの順序で考慮されます。2 番目の引数がグラウンドの場合、再帰的にアクセスされます。それ以外の場合、3 番目の引数が Ground の場合は、チェックされてから再帰的にアクセスされます。それ以外の場合は、2 番目と 3 番目の引数に対して適切なリストが生成されます。

occurrences(X, L, Os) :-
  ( nonvar(L) -> occs(L, X, Os) ;
    ( nonvar(Os) -> check(Os, X), glist(Os, X, L) ; v_occs(L, X, Os) ) ).

第 2 引数への訪問には、リストが空ではない場合が 3 つありXますXXそれ以外の場合は、頭とは異なるか、頭と一体化するという2 つの選択肢があります。

occs([],_,[]).
occs([X|R], Y, ROs) :-
  ( X==Y -> ROs=[X|Rr] ; foccs(X, Y, ROs, Rr) ), occs(R, Y, Rr).

foccs(X, Y, ROs, ROs) :- dif(X, Y).
foccs(X, X, [X|ROs], ROs).

グラウンドの 3 番目の引数をチェックすることは、そのすべてのメンバーが と統一されていることを確認することにありXます。原則として、このチェックは によって実行できますglist/3が、この方法では未使用の選択ポイントが回避されます。

check([], _).
check([X|R], X) :- check(R, X).

X自由な 2 番目の引数を持つ地面の 3 番目の引数への訪問は、生成されたリストに異なる変数を追加することによって終了する必要があります。各再帰ステップには 2 つの選択肢があります: 生成されたリストの現在の先頭は、訪問済みリストの現在の先頭であり、 で単一化可能であるか、Xまたは とは異なる自由変数ですX。これは理論的な説明にすぎません。実際には無限の数の解があり、リストの先頭が変数の場合は第 3 節に到達することはないからです。したがって、使用できない選択ポイントを避けるために、以下の 3 番目の節はコメント アウトされています。

glist([], X, L) :- gdlist(L,X).
glist([X|R], X, [X|Rr]) :- glist(R, X, Rr).
%% glist([X|R], Y, [Y|Rr]) :- dif(X, Y), glist([X|R], Y, Rr).

gdlist([], _).
gdlist([Y|R], X) :- dif(X, Y), gdlist(R, X).

最後に、すべての引数が自由であるケースは、前のケースと同様の方法で処理され、いくつかのソリューション パターンが実際には生成されないという同様の問題があります。

v_occs([], _, []).
v_occs([X|R], X, [X|ROs]) :- v_occs(R, X, ROs).
%% v_occs([X|R], Y, ROs) :- dif(Y, X), v_occs(R, Y, ROs). % never used

サンプルテスト

?- occurrences(1,[E1,1,2,1,E2],Fs).
Fs = [1,1],
dif(E1,1),
dif(E2,1) ? ;
E2 = 1,
Fs = [1,1,1],
dif(E1,1) ? ;
E1 = 1,
Fs = [1,1,1],
dif(E2,1) ? ;
E1 = E2 = 1,
Fs = [1,1,1,1] ? ;
no

?- occurrences(1,L,[1,2]).
no

?- occurrences(1,L,[1,E,1]).
E = 1,
L = [1,1,1] ? ;
E = 1,
L = [1,1,1,_A],
dif(1,_A) ? ;
E = 1,
L = [1,1,1,_A,_B],
dif(1,_A),
dif(1,_B) ? ;
   ...
于 2015-04-18T14:49:14.733 に答える