Prolog:使用其他列表的重复元素构建列表

时间:2015-10-16 22:32:31

标签: list prolog

我需要构造一个接收列表的谓词,检查谁是重复元素并返回其他列表。例如:

?- rep_elements([a,b,c,a,b,d], Xs).
Xs = [a,b].

我开始构建一个基本结构,但我现在不知道如何完成这个谓词,或者如果这是更好的方法:

exists(Elem, [Elem|_]).
exists(Elem, [H|T]) :-
    exists(Elem, T).

rep_elements([], []).
rep_elements([H|T], [Y|Z]) :-
    exists(H, T),
    rep_elements(T, Z).

有什么建议吗?

4 个答案:

答案 0 :(得分:3)

让我们一步一步地重新编写代码!

  1. 谓词exists/2由广泛使用的member/2涵盖。让我们使用那个一个!

    rep_elements([], []).
    rep_elements([H|T], [Y|Z]) :-
       member(H, T),
       rep_elements(T, Z).
    
  2. As @CapelliC saidrep_elements/2缺少一个条款;我们使用non_member/2添加一个!

    rep_elements([H|T], Z) :-
       non_member(T,H),
       rep_elements(T,Z).
    
  3. 让我们重新运行OP给出的查询!

    ?- rep_elements([a,b,c,a,b,d],Xs).
      Xs = [a,b]
    ; false.
    
  4. OK!我们完了吗?不完全的!请考虑以下查询和我们得到的答案:

    ?- rep_elements([a,a,a,a,b],Xs).
      Xs = [a,a,a]                 % (1) duplicate items are retained
    ; Xs = [a,a,a]                 % (2) redundant answer
    ; Xs = [a,a,a]                 % (2)
    ; Xs = [a,a,a]                 % (2)
    ; Xs = [a,a,a]                 % (2)
    ; Xs = [a,a,a]                 % (2)
    ; false.                       % (3) leaves behind useless choicepoint
    
  5. 下一步是什么?退后一步,制定规范!

答案 1 :(得分:3)

首先,我建议使用更具描述性的谓词名称list_uniqdups

我们根据 tpartition/4if_/3(=)/3定义list_uniqdups/2

list_uniqdups([],[]).
list_uniqdups([X|Xs0],Ys0) :-
   tpartition(=(X),Xs0,Es,Xs),
   if_(Es=[], Ys0=Ys, Ys0=[X|Ys]),
   list_uniqdups(Xs,Ys).

示例查询:

?- list_uniqdups([a,b,c,a,b,d],Xs).  % query as given by the OP
Xs = [a,b].                          % expected result

?- list_uniqdups([a,c,b,a,b,d],Xs).  % similar query
Xs = [a,b].                          % same result

?- list_uniqdups([b,c,a,a,b,d],Xs).  % now, `b` comes before `a`
Xs = [b,a].                          % retain the original order

?- list_uniqdups([a,a,a,a,b],Xs).
Xs = [a].                            % remove all duplicates

请注意,以上所有查询都会确定性地成功。

答案 2 :(得分:2)

rep_elements / 2缺少对非重复元素的处理。

如果exists / 2在删除找到的重复项后也会“返回”列表T,那么我们可以使用清理后的列表进行递归,任务就完成了。当然,exists / 2应该变为exists / 3,并且可能重命名为更好,更具描述性的名称。

答案 3 :(得分:0)

您可以递归检查列表的头部是否在列表的尾部,然后将其添加到Result。 您的解决方案可以修改为:

exists(Elem, [Elem|_]).
exists(Elem, [H|T]) :-
     exists(Elem, T).
/* IF you get empty list*/
rep_elements([], []).

/* If repetition found in list then joining it to head of list*/
rep_elements([H|T], Result) :-
     exists(H, T),
     rep_elements(T, [H|Result]).

/* If head of list is not found in the Tail of list*/
rep_elements(H|T, Result):-
     not(exists(H,T)),
     rep_elements(T,Result).

这应该有用。