删除列表中的重复项目(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/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
:
? - 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列表中的顺序相同。