我正在尝试从列表中删除重复项,同时保持最右边的出现次数。例如:[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).
答案 0 :(得分:3)
我是纯洁的还是不纯洁的?如果我们能够轻易地保存它,为什么甚至会考虑牺牲logical-purity?
使用memberd_t/3
和if_/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
在上面的查询1
中2
位于列表[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
是不可判定的。
为了节省逻辑健全性,prolog-dif约束的执行被延迟。
最后,还有一个用例:"输入"列表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的后续行动...在此回答中,我们使用dcg!
我们在memberd_t/3
和if_//3
- dcg类似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.