3

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

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

3 回答 3

3

我是纯洁的还是不纯洁的?如果我们可以轻松保存它, 为什么还要考虑牺牲
使用memberd_t/3and if_/3,我们定义list_rset/2和它的“孪生” list_lset/2

list_r设置 [],[])。% 保持右边的出现
list_rset([E|Es], Rs0) :-
   if_(memberd_t(E, Es),
       Rs0 = Rs,
       Rs0 = [E|Rs]),
   list_rset(Es, Rs)。

list_l set( [ ], [])。% 保留左边的出现
list_lset([E|Es], Ls) :-
   post_pre_lset(Es, [E], Ls)。% 使用内部辅助谓词

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]。% 确定性地成功

在上面的查询中,列表的开头结尾都在1前面。如果在开头在前面但在结尾跟随(例如,)怎么办?2[1,2,3,1,2]
12[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]。% 确定性地成功

接下来,我们看一个更一般的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)。
于 2015-10-21T20:23:45.923 回答
1

这是上一个答案的后续行动......在这个答案中,我们使用

我们建立lset//1memberd_t/3if_//3的 dcg类似物if_/3

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
于 2015-10-23T12:42:37.763 回答
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.
于 2015-10-21T19:03:02.707 回答