删除列表中的重复项(Prolog)

时间:2010-02-14 10:49:25

标签: list prolog

我是Prolog的新手,并尝试了一些练习。其中之一是:

  

编写谓词集(InList,OutList)   作为输入是任意的   列表,并返回每个列表   输入列表的元素仅出现   一次。

这是我的解决方案:

member(X,[X|_]).
member(X,[_|T]) :- member(X,T).

set([],[]).
set([H|T],[H|Out]) :-
    not(member(H,T)),
    set(T,Out).
set([H|T],Out) :-
    member(H,T),
    set(T,Out).

我不允许使用任何内置谓词(即使不使用not/1也会更好)。问题是,set/2提供了多个相同的解决方案。输入列表中的重复次数越多,解决方案就越多。我究竟做错了什么?提前谢谢。

9 个答案:

答案 0 :(得分:10)

由于Prolog的回溯,您将获得多种解决方案。从技术上讲,提供的每个解决方案都是正确的,这就是生成它的原因。如果您只想生成一个解决方案,那么您将不得不在某个时刻停止回溯。这就是Prolog cut的用途。你可能会发现阅读它会帮助你解决这个问题。

更新:对。如果第一个变量位于第二个变量的多个位置,则member()谓词会以多种不同的方式评估为true

我为这个谓词使用了名称mymember(),以免与GNU Prolog的内置member()谓词冲突。我的知识库现在看起来像这样:

mymember(X,[X|_]).
mymember(X,[_|T]) :- mymember(X,T).

not(A) :- \+ call(A).

set([],[]).
set([H|T],[H|Out]) :-
    not(mymember(H,T)),
    set(T,Out).
set([H|T],Out) :-
    mymember(H,T),
    set(T,Out).

因此,mymember(1, [1, 1, 1]).以三种不同的方式评估为true

| ?- mymember(1, [1, 1, 1]).

true ? a

true

true

no

如果您只想获得一个答案,那么您将不得不使用剪辑。将mymember()的第一个定义更改为:

mymember(X,[X|_]) :- !.

解决您的问题。

此外,如果您愿意,可以通过自己定义not()谓词来完全避免notamember()。选择是你的。

答案 1 :(得分:7)

你走在正确的轨道上...... 保持纯洁 ---这很容易!

Prolog union for A U B U C中实施的=/3dif/3结合使用具体化的平等谓词if_/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). % dif/3 is defined like (=)/3 dif(X, Y, R) :- X == Y, !, R = false. dif(X, Y, R) :- ?=(X, Y), !, R = true. % syntactically different dif(X, Y, R) :- X \= Y, !, R = true. % semantically different dif(X, Y, R) :- R == true, !, X \= Y. dif(X, Y, true) :- % succeed first! dif(X, Y). dif(X, X, false). if_(C_1, Then_0, Else_0) :- call(C_1, Truth), functor(Truth,_,0), % safety check ( Truth == true -> Then_0 ; Truth == false, Else_0 ).

list_item_isMember/3

基于这些谓词,我们构建了一个具体化的成员谓词memberd_truth/3。它与@false在语义上等同于memberd_truth/3。我们重新排列参数顺序,因此列表是第一个参数。这样就可以启用第一个参数索引,以防止在list_item_isMember([],_,false). list_item_isMember([X|Xs],E,Truth) :- if_(E = X, Truth = true, list_item_isMember(Xs,E,Truth)). list_set([],[]). list_set([X|Xs],Ys) :- if_(list_item_isMember(Xs,X), Ys = Ys0, Ys = [X|Ys0]), list_set(Xs,Ys0). 创建后留下无用的选择点。

set/2

一个简单的查询显示所有冗余答案已被删除并且目标成功而不留下任何选择点

?- list_set([1,2,3,4,1,2,3,4,1,2,3,1,2,1],Xs).
Xs = [4,3,2,1].                         % succeeds deterministically

编辑2015-04-23

我受到了@Ludwig对set([],[]). set([H|T],[H|T1]) :- subtract(T,[H],T2), set(T2,T1). 的回答的启发,其内容如下:

subtract/3

SWI-Prolog的内置谓词list_item_subtracted/3可能是非单调的,这可能会限制其使用。 list_item_subtracted([],_,[]). list_item_subtracted([A|As],E,Bs1) :- if_(dif(A,E), Bs1 = [A|Bs], Bs = Bs1), list_item_subtracted(As,E,Bs). 单调的变体:

list_setB/2

set/2list_item_subtracted/3类似,但基于subtract/3 ---而不是list_setB([],[]). list_setB([X|Xs1],[X|Ys]) :- list_item_subtracted(Xs1,X,Xs), list_setB(Xs,Ys).

list_set/2

以下查询会比较list_setB/2和{{1}}:

?- list_set([1,2,3,4,1,2,3,4,1,2,3,1,2,1], Xs).
Xs = [4,3,2,1].                          % succeeds deterministically
?- list_setB([1,2,3,4,1,2,3,4,1,2,3,1,2,1],Xs).
Xs = [1,2,3,4].                          % succeeds deterministically

?- list_set(Xs,[a,b]).
   Xs = [a,b]
;  Xs = [a,b,b]
;  Xs = [a,b,b,b] 
...                                      % does not terminate universally
?- list_setB(Xs,[a,b]).
   Xs = [a,b]
;  Xs = [a,b,b]
;  Xs = [a,b,b,b]
...                                      % does not terminate universally

答案 2 :(得分:6)

更简单(也可能更快)的解决方案是使用库谓词sort / 2来删除O(n log n)中的重复项。绝对适用于Yap prolog和SWIPL

答案 3 :(得分:4)

我认为更好的方法是:

set([], []).
set([H|T], [H|T1]) :- subtract(T, [H], T2), set(T2, T1).

因此,例如?- set([1,4,1,1,3,4],S)为您提供输出:

S = [1, 4, 3]

答案 4 :(得分:1)

将我的答案添加到这个旧帖子中:

notmember(_,[]).
notmember(X,[H|T]):-X\=H,notmember(X,T).

set([],[]).
set([H|T],S):-set(T,S),member(H,S).
set([H|T],[H|S]):-set(T,S),not(member(H,S)).

此解决方案的唯一是它仅使用在original text中出现此练习的点引入的谓词。

答案 5 :(得分:0)

这没有削减,但它需要更多的行和另一个参数。 如果我在第3行将[H2 | T2]更改为S,则会产生多个结果。我不明白为什么。

setb([],[],_).
setb([H|T],[H|T2],A) :- not(member(H,A)),setb(T,T2,[H|A]).
setb([H|T],[H2|T2],A) :- member(H,A),setb(T,[H2|T2],A).
setb([H|T],[],A) :- member(H,A),setb(T,[],A).
set(L,S) :- setb(L,S,[]).

答案 6 :(得分:0)

你只需要停止Prolog的回溯。

enter code here
member(X,[X|_]):- !.
member(X,[_|T]) :- member(X,T).

set([],[]).
set([H|T],[H|Out]) :-
   not(member(H,T)),
   !,
set(T,Out).
set([H|T],Out) :-
   member(H,T),
   set(T,Out).

答案 7 :(得分:0)

/* Remove duplicates from a list without accumulator */
our_member(A,[A|Rest]).
our_member(A, [_|Rest]):-
    our_member(A, Rest).

remove_dup([],[]):-!.
remove_dup([X|Rest],L):-
    our_member(X,Rest),!,
    remove_dup(Rest,L).
remove_dup([X|Rest],[X|L]):-
    remove_dup(Rest,L).

答案 8 :(得分:-1)

使用Tim的支持函数mymember,如果集合中元素的顺序不重要,则可以这样做:

mymember(X,[X|_]).
mymember(X,[_|T]) :- mymember(X,T).

mkset([],[]).
mkset([T|C], S) :- mymember(T,C),!, mkset(C,S).
mkset([T|C], S) :- mkset(C,Z), S=[T|Z].

因此,例如?- mkset([1,4,1,1,3,4],S)为您提供输出:

S = [1, 3, 4]

但是,如果你想要一个像列表一样订购的元素的集合,你可以使用:

mkset2([],[], _).
mkset2([T|C], S, D) :- mkset2(C,Z,[T|D]), ((mymember(T,D), S=Z,!) ; S=[T|Z]).
mkset(L, S) :- mkset2(L,S,[]).

此解决方案与前一个示例的输入相同,为您提供:

S = [1, 4, 3]

这次元素的顺序与它们在输入列表中的显示顺序相同。