使用不需要的尾部删除列表结果中的重复项

时间:2013-02-19 13:56:46

标签: lambda prolog

我想删除给定列表中的所有重复项。

请考虑以下代码:

% check if the given element is in the given list 
member(Element,[Element|_]).
member(Element,[_|List]):-member(Element, List).


% append the element only if it's NOT already in the input list 
appending([],X,X).
appending([H|T1],Elem,[H|T2]):- appending(T1,Elem,T2).

appendHlp(ListOrg,Res,Addme):- not(member(Addme,ListOrg)),
                   appending(ListOrg,[Addme],Res).
appendHlp(ListOrg,Res,Addme):- member(Addme,ListOrg),
                   Res=ListOrg.

% remove duplicates 
setify([H|T],Set):-appendHlp(Set,Output,H),
           setify(T,Output).
setify([],_).

当我运行代码时:

1 ?- setify([1,2,3,3,2],X).

输出结果为:

X = [1, 2, 3|_G2725] 

如何删除尾巴?

谢谢

5 个答案:

答案 0 :(得分:4)

您的问题(表面上看)是使用未实例化的变量调用member / 2,然后“构建”您在输出中看到的部分列表

[trace]  ?- setify([1],X).
   Call: (6) setify([1], _G340) ? creep
   Call: (7) appendHlp(_G340, _G416, 1) ? creep
^  Call: (8) not(member(1, _G340)) ? creep
^  Fail: (8) not(user:member(1, _G340)) ? creep
   Redo: (7) appendHlp(_G340, _G416, 1) ? creep
   Call: (8) lists:member(1, _G340) ? creep
   Exit: (8) lists:member(1, [1|_G409]) ? creep
   Call: (8) _G418=[1|_G409] ? creep
   Exit: (8) [1|_G409]=[1|_G409] ? creep
   Exit: (7) appendHlp([1|_G409], [1|_G409], 1) ? creep
   Call: (7) setify([], [1|_G409]) ? creep
   Exit: (7) setify([], [1|_G409]) ? creep
   Exit: (6) setify([1], [1|_G409]) ? creep
X = [1|_G409] .

要纠正起来并不容易,因为您使用非常错综复杂的方式来执行非常简单的任务。例如,如果您使用memberchk替换成员,或使用member(Addme,ListOrg)替换[Addme]=ListOrg,则您的程序将失败。

我建议采取Prolog社区接受的一些编程习惯。例如,尝试在输出之前放置输入参数。它并不总是可能的,因为Prolog是关系,但有助于编写更好的代码。

当然,sort / 2可以有效地产生输出而且没有麻烦。

编辑解决方案可以非常紧凑,基本的Prolog:

setify([E|R], U) :- memberchk(E, R), !, setify(R, U).
setify([E|R], [E|U]) :- setify(R, U).
setify([], []).

3 ?- setify([1,1,1,2,1,1],L).
L = [2, 1].

答案 1 :(得分:4)

如果你使用SWI-Prolog,你可以写

:- use_module(library(lambda)).

setify(L, Set) :-
    foldl(\X^Y^Z^(memberchk(X, Y)->Z=Y; append(Y, [X], Z)), L, [], Set).

答案 2 :(得分:2)

正如CapelliC所说,你可以使用sort/2 - 它将删除重复项并对元素进行排序。集合通常以排序的形式表示。

如果你需要保留元素的原始顺序,这里不是非常有效但非常简单的实现:

setify([H|T], OldSet, NewSet) :-
    ( \+ memberchk(H, OldSet) ->
        append(OldSet, [H], CurrentSet),
        setify(T, CurrentSet, NewSet)
    ;
        setify(T, OldSet, NewSet)
    ).
setify([], OldSet, OldSet).

setify(List, Set) :-
    setify(List, [], Set).

答案 3 :(得分:1)

使用尾递归和单个谓词来完成整个事情的另一个解决方案,但不保留原始顺序。

% Finish recursing / empty list
remdup([], []).

% Recurse tail first, don't add H to the list if already included
remdup([H|T], NoDups) :-
    remdup(T, NoDups),
    member(H, NoDups).

% Second rule failed, so H must be unique - add to NoDups list
remdup([H|T], [H|NoDups]) :-
    remdup(T, NoDups).


?- remdup([1,2,2,3,2,1,3,3,3,2],X).
X = [1, 3, 2] .

答案 4 :(得分:1)

我对Prolog很新。这是我想出的一个解决方案,用于删除重复的元素。希望这可以帮助。

% Define negation of "member"
notmember(X,L) :- not( member(X,L) ).

% Remove duplicates terminal case
removeDups( [], R, R).

% Case where the head element is a member of tail. Drop it.
removeDups( [H|T], R, A ) :- member(H,T) , removeDups( T, R, A ).

% Case where head element is unique
removeDups([H|T], R, A ) :- notmember(H,T), append(A,[H],N), removeDups(T,R,N).

% Main predicate to remove duplicates with original order maintained.
uniq(X,Y) :- removeDups(X,Y,[]).