从列表中删除重复项,同时保持最右侧的出现次数

时间:2015-10-21 17:34:27

标签: list prolog prolog-dif

我正在尝试从列表中删除重复项,同时保持最右边的出现次数。例如:[1,2,3,1,2][3,1,2]转换 这是我在Prolog的第一次尝试之一,我不明白我做错了什么。它总是返回false。这是我的代码:

%nrap(L:list,E:element,S:integer)
%L - the initial list, list of integers
%E - the element, integer
%S - the result, nrap of E in L, S integer
%flow model: (i,i,o),(i,i,i)

nrap([],_,0).
nrap([H|T],E,S):-
    H=E,
    nrap(T,E,S1),
    S is S1+1.
nrap([H|T],E,S):-
    H\=E,
    nrap(T,E,S).

%transform(L:list,L2:list,R:list)
%L - the initial list, list of integers
%L2 - copy of the initial list
%R - the resulted list, without duplicates, list of integers
%flow model: (i,i,o),(i,i,i)

transform([],[],[]).
transform([H|T],L2,[H|R]):-
    nrap(L2,H,S),
    S=1,
    transform(T,L2,R).
transform([H|T],L2,R):-
    nrap(L2,H,S),
    S>1,
    transform(T,L2,R).

3 个答案:

答案 0 :(得分:3)

我是纯洁的还是不纯洁的?如果我们能够轻易地保存它,为什么甚至会考虑牺牲
使用memberd_t/3if_/3,我们定义list_rset/2及其" twin" list_lset/2

list_rset([], []).                     % keep rightmost occurrences
list_rset([E|Es], Rs0) :-
   if_(memberd_t(E, Es),
       Rs0 = Rs,
       Rs0 = [E|Rs]),
   list_rset(Es, Rs).

list_lset([], []).                     % keep leftmost occurrences
list_lset([E|Es], Ls) :-
   post_pre_lset(Es, [E], Ls).         % uses internal auxilary predicate

post_pre_lset([], _, []).            
post_pre_lset([E|Es], Pre, Ls0) :-     % 2nd arg: look-behind accumulator
   if_(memberd_t(E, Pre),
       Ls0 = Ls,
       Ls0 = [E|Ls]),
   post_pre_lset(Es, [E|Pre], Ls).

让我们运行一些查询!

?- _Es = [1,2,3,1,2], list_lset(_Es, Ls), list_rset(_Es, Rs).
Ls = [1,2,3], Rs = [3,1,2].           % succeeds deterministically

在上面的查询12位于列表[1,2,3,1,2]末尾的的开头。
如果1在开始时位于2之前但在结尾处跟随它(例如[1,2,3,2,1])会怎样?

?-  _Es = [1,2,3,2,1], list_lset(_Es, Ls), list_rset(_Es, Rs).
Ls = [1,2,3], Rs = [3,2,1].          % succeeds deterministically

接下来,我们将看一个更一般的list_rset/2目标,它使用仅包含变量的列表。
感谢@PauloMoura的建议!

?- Es = [A,B,C,A,B], list_rset(Es,Rs).
   Es = [C,C,C,C,C], Rs = [    C],     A=B ,               B=C
;  Es = [B,B,C,B,B], Rs = [C,  B],     A=B ,           dif(B,C)
;  Es = [C,B,C,C,B], Rs = [  C,B],               A=C , dif(B,C)
;  Es = [A,C,C,A,C], Rs = [  A,C],           dif(A,C),     B=C
;  Es = [A,B,C,A,B], Rs = [C,A,B], dif(A,B), dif(A,C), dif(B,C).

剩下的目标(上图)是什么? 如果没有足够的实例化,dif/2是不可判定的。
为了节省逻辑健全性,约束的执行被延迟。

最后,还有一个用例:"输入"列表Xs包含变量基础术语。

?- Es = [A,B,z], list_rset(Es,Rs).
   Es = [z,z,z], Rs = [    z],     A=B ,               B=z 
;  Es = [B,B,z], Rs = [B,  z],     A=B ,           dif(B,z)
;  Es = [z,B,z], Rs = [  B,z],               A=z , dif(B,z)
;  Es = [A,z,z], Rs = [A,  z],           dif(A,z),     B=z 
;  Es = [A,B,z], Rs = [A,B,z], dif(A,B), dif(A,z), dif(B,z).

答案 1 :(得分:1)

这是this previous answer的后续行动...在此回答中,我们使用

我们在memberd_t/3if_//3 - 类似if_/3上构建lset//1

lset([]) -->
   [].
lset([X|Xs]) -->
   [X],
   lset_pre(Xs,[X]).

lset_pre([],_) -->
   [].
lset_pre([X|Xs],Pre) -->
   if_(memberd_t(X,Pre), [], [X]),
   lset_pre(Xs,[X|Pre]).

rset//1相同:

rset([]) -->
   [].
rset([X|Xs]) -->
   if_(memberd_t(X,Xs), [], [X]),
   rset(Xs).

一些示例查询:

?- _Es = [1,2,3,1,2], phrase(lset(_Es),Ls), phrase(rset(_Es),Rs).
Ls = [1,2,3], Rs = [3,1,2].               % succeeds deterministically

?- _Es = [1,2,3,2,1], phrase(lset(_Es),Ls), phrase(rset(_Es),Rs).
Ls = [1,2,3], Rs = [3,2,1].               % succeeds deterministically

答案 2 :(得分:0)

这比你做的容易。由于"设置"中的元素必须按照最后外观的顺序,您根本不需要保留列表的副本:只需与列表的其余部分(尾部)进行比较。

如果您知道第一个列表始终是基础(例如,所有元素都是整数),您可以写:

list_set([], []).
list_set([X|Xs], Ys0) :-
    (   memberchk(X, Xs)
    ->  Ys0 = Ys
    ;   Ys0 = [X|Ys]
    ),
    list_set(Xs, Ys).

memberchk/2可用于检查基础术语是否在基础术语列表中。它会成功或失败一次。

更一般的解决方案是提出一个约束,即如果元素与其后面的所有元素不同,则该元素应该在集合中,否则将被删除:

list_set([], []).
list_set([X|Xs], [X|Ys]) :-
    maplist(dif(X), Xs),
    list_set(Xs, Ys).
list_set([X|Xs], Ys) :-
    \+ maplist(dif(X), Xs),
    list_set(Xs, Ys).

此处,maplist(dif(X), Xs)表示:

  

X与列表Xs(尾部)中的每个元素都不同。

\+ Goal成功,然后Goal没有成功。

有了这个定义:

?- list_set([1,2,3,1,2], S).
S = [3, 1, 2] ;
false.

?- list_set([1,2,3,3,1,1,2], S).
S = [3, 1, 2] ;
false.

?- list_set([A,B,C,A,B],Xs).
Xs = [C, A, B],
dif(A, B),
dif(C, B),
dif(C, A) ;
false.