9

値とリストを指定するプロシージャを作成しようとすると、作成したリスト内のその値のすべての出現が削除されます。

delMember(X, [], []) :- !.
delMember(X, [X|Xs], Y) :- !, delMember(X, Xs, Y).
delMember(X, [T|Xs], Y) :- !, delMember(X, Xs, Y2), append([T], Y2, Y).

cutこのコードは次のようなクエリに正しく答えることができないため:

delMember(Y, [1,2,3,1,2,3,1,2,3], [1, 2, 1, 2, 1, 2 ]).

カットを削除すると:

delMember(X, [], []).
delMember(X, [X|Xs], Y) :- delMember(X, Xs, Y).
delMember(X, [T|Xs], Y) :- delMember(X, Xs, Y2), append([T], Y2, Y).

次のようなクエリでは失敗します。

delMember(Y, [1,2,3,1,2,3,1,2,3], [1,2,3,1,2,3,1,2,3]).

true(正解が の場合、 を返しますfalse)。

両方の状況で機能させるにはどうすればよいですか?

X is not Tコードの 3 行目で確認できるかもしれませんが、試してみました。

delMember(X, [T|Xs], Y) :- not(X = T), delMember(X, Xs, Y2), append([T], Y2, Y).

しかし、それは機能しません。

4

4 に答える 4

10

カットの使用

delMember(X, [], []) :- !.
delMember(X, [X|Xs], Y) :- !, delMember(X, Xs, Y).
delMember(X, [T|Xs], Y) :- !, delMember(X, Xs, Y2), append([T], Y2, Y).

!/0ここで、述語の最後の節で使用していることがわかります。それは必要はありません。最後の句の後には選択肢が残っていない (Prolog は左から右へ、上から下への選択ポイントを記憶する) ため、カット (選択肢を削除する) は、リストの一番下にいるため、何の役にも立ちません。選択の。

具体的には、次を参照してください。

a :- b; c.
a :- d.

ここで、 を証明するためaに、Prolog は最初に 、次に 、(左から右、次に上から下) を試行bcますd

ところで、Prolog の初心者として、カットの使用を完全に避けるべきです。再帰やその他の論理プログラミングの基本を理解していない限り、誤解が増えるだけです。

プロローグ再帰

ちょっとしたメモはさておき、あなたの問題は、Prolog の再帰をまだ正しく理解していないことです。この懸念にすでに対処しているこの回答の最初の部分を参照してください。

あなたの3番目の節は間違っています:

delMember(X, [T|Xs], Y) :- delMember(X, Xs, Y2), append([T], Y2, Y).

それは読むべきです:

delMember(X, [T|Xs], [T|Y]) :- delMember(X, Xs, Y).

まあ、それは本当に間違っているわけではありません。これは末尾再帰ではなくappend/3、 を使用します。これにより、線形述語が二次述語に変わります。さらに、お気づきのように、末尾再帰ではないため、場合によっては終了を取得するのが難しくなります。

次に、カットの使用を削除するために、最後の句にガード!/0を追加することを検討できます。

delMember(_, [], []).
delMember(X, [X|Xs], Y) :-
    delMember(X, Xs, Y).
delMember(X, [T|Xs], [T|Y]) :-
    dif(X, T),
    delMember(X, Xs, Y).

ガード ,dif(X, T)は、3 番目のケースにいる場合、同時に 2 番目のケースにいることはできないことを指定します:ここXでは統合できませんT

述語を使用できない方法がまだ 1 つあります。これは、+, -, +cTI教えてくれるように、 です。したがって、次のようなクエリ?- delMember(1, R, [2, 3]).は私のバージョンでループします。

お役に立てば幸いです。

于 2012-08-29T10:29:23.203 に答える
4

少し言い換えてみましょう。述語を複数のインスタンス化パターンで使用したいので、「値とリストを指定したプロシージャは、リスト内のその値のすべての出現を削除します」は、それがどのように動作するかを定義しませんその他の場合。したがって、「2番目と3番目の引数がリストL1の場合、述語はtrueであり、最初の引数のすべての出現を無視すると、L1はL2と同じリストになります」のようなものが必要になる可能性があります。

現在、複数の可能なインスタンス化を使用して述語を作成する方法は2つあります。var/1andなどのメタロジカル述語を使用してground/1、それぞれにコードを記述したり(おそらく、その特定のインスタンス化に最適化されたコードを記述したりできます)、プロパティを論理的に定義するコードを記述したりできます(これはより困難な場合があります)。

この場合、次のようなことができます。

    del(_, [], []).
    del(X, [X|L1], L2):-
        del(X,L1,L2).
    del(X, [H|L1], [H|L2]):-
        X\==H,
        del(X,L1,L2).

これは次の動作をします:

19 ?- del(1, [1,2,3], X).
X = [2, 3] ;
false.
1,2,3,
20 ?- del(1, [1,2,3], [2,3]).
true ;
false.

21 ?- del(X, [1,2,3], [2,3]).
X = 1 ;
false.

22 ?- del(X, [1,2,3], Y).
X = 1,
Y = [2, 3] ;
X = 2,
Y = [1, 3] ;
X = 3,
Y = [1, 2] ;
Y = [1, 2, 3] ;
false.

23 ?- del(X, P, Y).
P = Y, Y = [] ;
P = [X],
Y = [] ;
P = [X, X],
Y = [] ;
P = [X, X, X],
Y = [] ;
P = [X, X, X, X],
Y = [] ;
P = [X, X, X, X, X],
Y = [] ;
P = [X, X, X, X, X, X],
Y = [] .

最後の呼び出しに関して; prologは、深さ優先アルゴリズムが使用されるために大きくなるXのリストを返します。を使用length/2することで、幅優先の結果を得ることができます(_Gは、変数がインスタンス化されていないことを意味します(何でもかまいません)):

24 ?- length(P,N), del(X, P, Y).
P = [],
N = 0,
Y = [] ;
P = [X],
N = 1,
Y = [] ;
P = [_G548],
N = 1,
Y = [_G548] ;
P = [X, X],
N = 2,
Y = [] ;
P = [X, _G551],
N = 2,
Y = [_G551] ;
P = [_G548, X],
N = 2,
Y = [_G548] ;
P = [_G548, _G551],
N = 2,
Y = [_G548, _G551] ;
P = [X, X, X],

編集:@chacが指摘したように、最初のリストに(少なくとも)1つの重複要素がある場合、上記の述語は正しく動作しません。

?- del(X,[1,2,1],Y).
X = 1,
Y = [2] ;
X = 2,
Y = [1, 1] ;
X = 1,
Y = [1, 2] ;                  <----- wrong
Y = [1, 2, 1].

これは、実際には変数に制限を課していない\==/2ためです。\=/2これは、3番目の句のルールの順序を切り替えることで解決できます。

    del(_, [], []).
    del(X, [X|L1], L2):-
        del(X,L1,L2).
    del(X, [H|L1], [H|L2]):-
        del(X,L1,L2),
        X\==H.


4 ?- del(X,[1,2,1],Y).
X = 1,
Y = [2] ;
X = 2,
Y = [1, 1] ;
Y = [1, 2, 1] ;
false.

ただし、これは、述語が末尾再帰ではなくなったことを意味します。Xであってはならない値のリストを保持できることを修正するには、次のようにします。

del(X,L1,L2):-
    del(X,L1,L2,[]).

del(X, [], [], NotX):-
    \+ member(X,NotX).
del(X, [X|L1], L2, NotX):-
    del(X,L1,L2,NotX).
del(X, [H|L1], [H|L2], NotX):-
    X\==H,     % <--- optional; stops the execution earlier (saving time)
    del(X,L1,L2,[H|NotX]).

ただし、以下によると、末尾再帰バージョンは実際には低速です。

?-time(forall((between(1,50,N),length(X,N),del2(1,X,[2,3,2,3])),true)).
% 25,600,793 inferences, 5.468 CPU in 5.548 seconds (99% CPU, 4682134 Lips)
true.

?- time(forall((between(1,50,N),length(X,N),del_tr(1,X,[2,3,2,3])),true)).
% 37,346,143 inferences, 6.426 CPU in 6.428 seconds (100% CPU, 5811563 Lips)
true.

それでも、+-+は機能しません(無限ループに陥ります)。しかし、なぜ?問題は句の順序にあります。del(1、L1、[2])は、最初にXをL1の先頭に「追加」するルールを適用し、次に同じルールを永久に適用します。これは(再び)を使用して対抗することができますlength/2

?- length(X,2), del(1,X,[2]).
X = [1, 2] ;
X = [2, 1] ;
false.

または、句の順序を変更することもできます。

del(_, [], []).
del(X, [H|L1], [H|L2]):-
    X\==H,
    del(X,L1,L2),
    X\==H.
del(X, [X|L1], L2):-
    del(X,L1,L2).

length/2それがなければ、プロローグは深さ優先探索を行うので、まだ再び役立つかもしれません:

?- del(1,X,[2]).
X = [2] ;
X = [2, 1] ;
X = [2, 1, 1] ;
X = [2, 1, 1, 1] ;
X = [2, 1, 1, 1, 1] ;
X = [2, 1, 1, 1, 1, 1] ;
X = [2, 1, 1, 1, 1, 1, 1] 

もちろんlength/2、他のインスタンス化パターンに影響を与えないため、ラッパー述語に組み込むことができます。

于 2012-08-29T10:39:17.947 に答える
4

これは本当の答えではなく、MogthanosQRの答えへの拡張されたメモであり、コメントに収めるには長すぎます。そのような答えは賛成で有益ですが、カットの除去を再考する必要があります。検討:

delMember(_, [], []).
delMember(X, [X|Xs], Y) :-
    delMember(X, Xs, Y), !.
delMember(X, [T|Xs], [T|Y]) :-
    delMember(X, Xs, Y).

この定義により、

?- delMember(Y, [1,2,3,1,2,3,1,2,3], [1,2,1,2,1,2]).
Y = 3.

最後の原因でガードが原因で、元のMogのコードで失敗します。thanosQRで示されているように、ガードをX \== T(テストを一致するインスタンス化状態に制限する)に置き換えると、このクエリも解決されることに注意してください。

しかし、これらのスニペットはいずれも一般的なケースを解決しません。

?- del(X,[1,2,1],Y).
X = 1,
Y = [2] ;
X = 2,
Y = [1, 1] ;
X = 1,
Y = [1, 2] ;
Y = [1, 2, 1].
于 2012-08-29T12:21:41.070 に答える
0

インスタンス化されていない最初と 3 番目の引数でも機能するスニペットを次に示します。

delMember(X, Y, Z):-
  bagof(A, (setof(X, member(X, Y), L), member(X, L), member(A, Y), A\==X), Z).

ここでは、このコードが何をするのかを説明します。アイデアは次のとおりです。

  1. 入力メンバー X と統合する入力リスト Y の個別のメンバーのリストを作成します
  2. 次に、1) に基づいて作成されたリストの X ごとに、この要素を入力リストから破棄して、メンバ X を含まない出力リスト Z を取得します。

ステップ 1 は で完了しsetof(X, member(X, Y), L)、2 つの方法で機能します。パラメータXがすでにインスタンス化されている場合、が入力パラメータに含まれている場合はLリストになり、 が含まれていない場合は失敗します。一方、インスタンス化されていない場合は、入力パラメーターの個別の要素のセットになります。[X]XYXYXLY

ステップ 2 では、 のすべての要素をバックトラックし、このリストの各メンバーについて、結果を生成するL入力リストからこの要素をフィルター処理します。Yこのすべての要素を output list に収集しますZ

プロシージャが呼び出されたときにパラメーター X がインスタンス化されていない場合、バックトラックすると、フィルター処理に使用されるmember(X, Y)入力リストのすべてのメンバーが取得されることに注意してくださいY

テストケース:

?- delMember(Y, [1,2,3,1,2,3,1,2,3], [1,2,3,1,2,3,1,2,3]).
false.

?- delMember(Y, [1,2,3,1,2,3], X).
Y = 1,
X = [2, 3, 2, 3] ;
Y = 2,
X = [1, 3, 1, 3] ;
Y = 3,
X = [1, 2, 1, 2].

?- delMember(Y, [1,2,3,1,2,3,1,2,3], [1,2,1,2,1,2]).
Y = 3.

?- delMember(X,[1,2,1],Y).
X = 1,
Y = [2] ;
X = 2,
Y = [1, 1].
于 2012-08-29T13:21:23.053 に答える