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

我对Prolog完全陌生,并尝试一些练习。 其中之一是:

编写一个谓词集(InList,OutList),它将任意列表作为input,并返回一个列表,其中input列表的每个元素只出现一次。

这是我的解决scheme:

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给出了多个相同的解决scheme。 input列表中的重复次数越多,结果就越多。 我究竟做错了什么? 提前致谢。

由于Prolog的回溯,您可以获得多种解决scheme。 从技术上讲,所提供的每个解决scheme都是正确的,这就是为什么它正在生成。 如果你只想生成一个解决scheme,你将不得不在某个时候停止回溯。 这是Prolog 剪切的用途。 你可能会发现,阅读这将有助于你解决这个问题。

更新:正确。 如果第一个variables位于第二个variables的多个位置,则您的member()谓词将以多种不同方式评估为true

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

 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|_]) :- !. 

解决你的问题。

而且,如果你愿意的话,你可以通过自己定义notamember()谓词来完全避免not() 。 这是你的select。

你在正确的轨道上… 保持纯净 – 这很容易!

对于AUBUC , Prolog union中的@false使用了if_/3 equality谓词=/3 (又名equal_truth/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). if_(C_1, Then_0, Else_0) :- call(C_1, Truth), functor(Truth,_,0), % safety check ( Truth == true -> Then_0 ; Truth == false, Else_0 ). 

基于这些谓词,我们构build一个通用的成员谓词list_item_isMember/3 。 它在语义上等同于memberd_truth/3 。 我们重新排列参数顺序,所以列表是第一个参数。 这使得第一个参数索引可以避免在memberd_truth/3创build时留下无用的select点。

 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). 

一个简单的查询表明, 所有多余的答案已经被消除 ,并且目标成功, 没有留下任何select点

 ? -  list_set([1,2,3,4,1,2,3,4,1,2,3,1,2,1],Xs)。
 Xs = [4,3,2,1]。  % 确定性地成功

编辑2015-04-23

我受到@路德维希的答案set/2启发,这个答案是这样的:

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

SWI-Prolog的内build谓词subtract/3可能是非单调的,这可能会限制它的使用。 list_item_subtracted/3是它的单调变体:

 list_item_subtracted([],_,[]). list_item_subtracted([A|As],E,Bs1) :- if_(A = E, Bs = Bs1, Bs1 = [A|Bs]), list_item_subtracted(As,E,Bs). 

list_setB/2类似set/2 ,但基于list_item_subtracted/3subtract/3

 list_setB([],[]). list_setB([X|Xs1],[X|Ys]) :- list_item_subtracted(Xs1,X,Xs), list_setB(Xs,Ys). 

以下查询比较list_set/2list_setB/2

 ? -  list_set([1,2,3,4,1,2,3,4,1,2,3,1,2,1],Xs)。
 Xs = [4,3,2,1]。  % 确定性地成功
 ? -  list_setB([1,2,3,4,1,2,3,4,1,2,3,1,2,1],Xs)。
 Xs = [1,2,3,4]。  % 确定性地成功

人们可能更喜欢一个或另一个,这取决于Xs中列表项的所需顺序。

更简单(也可能更快)的解决scheme是使用库谓词sort / 2删除O(n log n)中的重复项。 肯定在Yap prolog和SWIPL中工作

我认为做一个更好的方法是:

 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] 

把我的答案添加到这个旧的线程:

 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)). 

这个解决scheme的唯一优点是它只使用原始文本中出现的那些谓词。

这可以毫无用处地工作,但需要更多的线条和另一个论点。 如果我在第三行将[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,[]). 

你只需要停止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). 

使用Tim的支持函数mymember ,如果集合中元素的顺序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,[]). 

这个解决scheme,与前面的例子相同的input给你:

 S = [1, 4, 3] 

这次元素的顺序与input列表中的顺序相同。