Prolog只删除唯一的元素
我想返回一个列表,例如删除所有独特的元素
remUniqueVals([1,1,2,2,3,4,4,5,6,6,6],Q). Q = [1,1,2,2,4,4,6,6,6].
我的问题是,目前我有代码返回
remUniqueVals([1,1,2,2,3,4,4,5,6,6,6],Q). Q = [1, 2, 4, 6, 6].
这样只返回这些非唯一值的第一个实例。 这是我的代码:
remUniqueVals([], []). remUniqueVals([Q1|RestQ],[Q1|Xs]) :- member(Q1,RestQ), remUniqueVals(RestQ,Xs). remUniqueVals([Q1|RestQ],Xs) :- remove(Q1,[Q1|RestQ], NewQ), remUniqueVals(NewQ,Xs).
我可以看到member(Q1,RestQ)
在第二次检查1,2,4时失败,因为他们现在不在列表中,因此将其删除。 我想要一些帮助解决这个问题,我的想法是检查member(Q1, PreviousQ)
,这是最后Q
的元素。 不知道如何去执行,虽然任何帮助,将不胜感激。
更新:
好的,谢谢你们提出的build议,
remUniqueVals(_,[], []). remUniqueVals(_,[Q1|RestQ],[Q1|Xs]) :- member(Q1,RestQ), remUniqueVals(Q1,RestQ,Xs). remUniqueVals(PrevQ,[Q1|RestQ],[Q1|Xs]) :- Q1 = PrevQ, remUniqueVals(PrevQ,RestQ,Xs). remUniqueVals(PrevQ,[_|RestQ],Xs) :- remUniqueVals(PrevQ,RestQ,Xs). remUniqueVals(0,[4,1,1,3,2,2,5,5],Q). Q = [1, 1, 2, 2, 5, 5]. remUniqueVals(0, [A,B,C], [1,1]). A = 1, B = 1, C = 1.
这与原始解决scheme类似,但它将辅助列表中的非唯一值收集起来并进行检查,以避免从原始中删除最后一个:
remove_uniq_vals(L, R) :- remove_uniq_vals(L, [], R). remove_uniq_vals([], _, []). remove_uniq_vals([X|T], A, R) :- ( member(X, A) -> R = [X|T1], A1 = A ; member(X, T) -> R = [X|T1], A1 = [X|A] ; R = T1, A1 = A ), remove_uniq_vals(T, A1, T1).
testing…
| ?- remove_uniq_vals([1,2,3,1,2,3,1,2,3,4,3], Q). Q = [1,2,3,1,2,3,1,2,3,3] (1 ms) yes | ?- remove_uniq_vals([1,1,2,2,3,4,4,5,6,6,6], Q). Q = [1,1,2,2,4,4,6,6,6] yes
所以如果第一个参数是一个input,谓词就很好,并且它保持列表中其余元素的原始顺序。
然而,这个谓词并不完全是相关的 ,因为它将会失败一个情况,即第一个参数是一个已知数量的元素的未被证实的列表,而第二个参数是不同固定数量元素的列表。 所以这样的事情会起作用:
| ?- remove_uniq_vals([A,B,C], L). B = A C = A L = [A,A,A] (1 ms) yes
但是像下面这样的失败:
| ?- remove_uniq_vals([A,B,C], [1,1]). no
Prolog规则是彼此独立读取的,所以你需要一个规则来处理元素是唯一的,而不是一个规则。 如果元素的顺序不相关,则可以使用:
?- remUniqueVals([A,B,C], [1,1]). A = B, B = 1, dif(C, 1) ; A = C, C = 1, dif(B, 1), dif(B, 1) ; B = C, C = 1, dif(A, 1), dif(A, 1) ; false. ?- remUniqueVals([1,1,2,2,3,4,4,5,6,6,6],Q). Q = [1, 1, 2, 2, 4, 4, 6, 6, 6] ; false. remUniqueVals([], []). remUniqueVals([Q1|RestQ],[Q1|Xs0]) :- memberd(Q1, RestQ), phrase(delall(Q1, RestQ, NewQ), Xs0, Xs), remUniqueVals(NewQ, Xs). remUniqueVals([Q1|RestQ],Xs) :- maplist(dif(Q1), RestQ), remUniqueVals(RestQ,Xs). memberd(X, [X|_Xs]). memberd(X, [Y|Xs]) :- dif(X,Y), memberd(X, Xs). delall(_X, [], []) --> []. delall(X, [X|Xs], Ys) --> [X], delall(X, Xs, Ys). delall(X, [Y|Xs], [Y|Ys]) --> {dif(X,Y)}, delall(X, Xs, Ys).
这里是memberd/2
一个替代定义,使用if_/3
可能更有效:
memberd(E, [X|Xs]) :- if_(E = X, true, memberd(E, Xs) ).
这是@ CapelliC解决scheme的另一个纯粹的关系解决scheme。 这一个现在保留重复的顺序。 有趣的是,如何在@ CapelliC的解决scheme中发生的隐式量化现在必须明确地进行。
拥有一个纯粹的关系定义的最大优点就是noes是noes。 而且是ayes。 那就是:你不必担心你所得到的答案是否正确。 这是正确的(或不正确的 – 但不是部分正确的)。 如果方法失败,则可以通过生成instantiation_error
来清理非关系型解决scheme。 但是,如果你可以validation自己,那么他们都已经“忘记”了这些testing,从而为错误做好准备。 对这些其他解决scheme的安全testing可能已经被研究ground(Xs)
或ground(Xs), acyclic_term(Xs)
但是经常被认为太受限制。
remUniqueVals2(Xs, Ys) :- tfilter(list_withduplicate_truth(Xs),Xs,Ys). list_withduplicate_truth(L, E, Truth) :- phrase( ( all(dif(E)), ( {Truth = false} | [E], all(dif(E)), ( {Truth = false} | {Truth = true}, [E], ... ) ) ), L). all(_) --> []. all(P_1) --> [E], {call(P_1,E)}, all(P_1). ... --> [] | [_], ... . tfilter( _, [], []). tfilter(TFilter_2, [E|Es], Fs0) :- call(TFilter_2,E,Truth), ( Truth = false, Fs0 = Fs ; Truth = true, Fs0 = [E|Fs] ), tfilter(TFilter_2, Es, Fs).
另一种更紧凑的方式使用if_/3
tfilter( _, [], []). tfilter(TFilter_2, [E|Es], Fs0) :- if_(call(TFilter_2,E), Fs0 = [E|Fs], Fs0 = Fs ), tfilter(TFilter_2, Es, Fs).
这是@ mbratch解决scheme的纯化版本。 它使用member/2
的重新版本,这个member/2
没有多余的答案member(X,[a,a])
。
memberd_truth_dcg(X, Xs, Truth) :- phrase(( all(dif(X)), ( [X], {Truth = true}, ... | {Truth = false} ) ), Xs).
一个稍微普遍的版本,只需要有一个列表前缀,而不是一个列表:
memberd_truth(_X, [], false). memberd_truth(X, [X|_], true). memberd_truth(X, [Y|Ys], Truth) :- dif(X,Y), memberd_truth(X, Ys, Truth).
variables的命名方式与@ mbratch解决scheme中的相同:
remove_uniq_valsBR(L, R) :- remove_uniq_valsBR(L, [], R). remove_uniq_valsBR([], _, []). remove_uniq_valsBR([X|T], A, R) :- memberd_truth(X, A, MemT1), ( MemT1 = true, R = [X|T1], A1 = A ; MemT1 = false, memberd_truth(X, T, MemT2), ( MemT2 = true, R = [X|T1], A1 = [X|A] ; MemT2 = false, R = T1, A1 = A ) ), remove_uniq_valsBR(T, A1, T1).
使用if/3
更紧凑:
remove_uniq_valsBR([], _, []). remove_uniq_valsBR([X|T], A, R) :- if_( memberd_truth(X, A), ( R = [X|T1], A1 = A ), if_( memberd_truth(X, T), ( R = [X|T1], A1 = [X|A] ), ( R = T1, A1 = A ) ) ) ), remove_uniq_valsBR(T, A1, T1).
我不喜欢的是许多冗余的dif/2
约束。 我希望这个版本会less一些:
| ?- length(L,_),remove_uniq_valsBR(L,L). L = [] ? ; L = [_A,_A] ? ; L = [_A,_A,_A] ? ; L = [_A,_A,_A,_A] ? ; L = [_A,_A,_B,_B], dif(_B,_A) ? ; L = [_A,_B,_A,_B], dif(_A,_B), dif(_B,_A), dif(_B,_A), dif(_A,_B) ? ...
当然可以检查一个dif/2
是否已经存在,但是我更喜欢一个版本,在这个版本中,从一开始就有一个dif/2
目标。
保留逻辑纯度 ! 基于if_/3
, (=)/3
和元谓词 tpartition/4
我们定义:
remUniqueValues([],[])。 remUniqueValues([X | Xs1],Ys1): - t分区 ( = (X),Xs1,Eqs,Xs0), if_ ( Eqs = [], Ys1 = Ys0, append ([X | Eqs],Ys0,Ys1)), remUniqueValues(Xs0,Ys0)。
让我们看看它的行动!
? - remUniqueValues([A,B,C],[1,1])。 A = 1,B = 1,dif(C,1) ; A = 1,dif(B,1),C = 1 ; dif(A,1),B = 1,C = 1 ; 假。 ? - remUniqueValues([1,1,2,2,3,4,4,5,6,6,6],Vs)。 Vs = [1,1,2,2,4,4,6,6,6]。 %确定性地成功
基于3个内置的解决scheme:
remUniqueVals(Es, NUs) :- findall(E, (select(E, Es, R), memberchk(E, R)), NUs).
可以看作是
find所有在列表中仍然出现在列表中的元素