如何在SWI Prolog中从列表中删除重复项?

如何在SWI Prolog中从列表中删除重复项?,prolog,Prolog,我需要编写一个谓词remove_duplicates/2,从给定列表中删除重复元素。例如: remove_duplicates([a,a,b,c],L). L = [a, b, c] ; false. ?-删除重复项([a,a,b,c,c],列表)。列表=[a、b、c]是 请记住,我只学习了两天的SWI Prolog,并且只了解Prolog的基础知识。这就是我目前的情况: remove_duplicates([H | T],List):-member(H,T),append(T,[],List

我需要编写一个谓词
remove_duplicates/2
,从给定列表中删除重复元素。例如:

remove_duplicates([a,a,b,c],L).
L = [a, b, c] ;
false.
?-删除重复项([a,a,b,c,c],列表)。列表=[a、b、c]是

请记住,我只学习了两天的SWI Prolog,并且只了解Prolog的基础知识。这就是我目前的情况:

remove_duplicates([H | T],List):-member(H,T),append(T,[],List1)

这适用于列表
[a,a,b,c]
,但不适用于尾部两个元素相同的列表。我想我必须以某种方式将Head移到一个临时列表中,创建一个新的Head,然后重复谓词。我不知道怎么做。此外,当头部不在尾部时,例如使用类似于
[a,b,b,c]
的列表时,终端只会说
False
,因为
成员(H,T)
不是真的


有什么想法吗?

谓词有一些问题:

remove_duplicates([H | T], List) :-
  member(H, T),
  append(T, [], List1).
首先,应该有一个递归谓词,检查列表的开头,但不递归检查列表的结尾:

因此,您可以将其更改为:

remove_duplicates([H | T], List) :- 
      member(H, T),
      remove_duplicates( T, List).
上面递归地调用尾部

其次,您必须决定如果h不是列表中的成员会发生什么,因此您需要添加另一个子句:

remove_duplicates([H | T], [H|T1]) :- 
       \+member(H, T), 
       remove_duplicates( T, T1).
上面检查T中是否存在H,如果不存在,则强制列表中要返回的第一个元素为元素H:

如果这一点不清楚,则上述内容等于:

  remove_duplicates([H | T], List) :- 
           \+member(H, T), 
           List=[Head|Tail],
           Head=H,
           remove_duplicates( T, Tail).
这不是很优雅,只是为了了解以前的作品

最后,您需要一个空列表递归的基:

remove_duplicates([],[]).
因此,根据上述条款,谓词remove_duplicates应为:

remove_duplicates([],[]).

remove_duplicates([H | T], List) :-    
     member(H, T),
     remove_duplicates( T, List).

remove_duplicates([H | T], [H|T1]) :- 
      \+member(H, T),
      remove_duplicates( T, T1).
尽管您必须注意,上面的每个元素只保留一次,例如:

remove_duplicates([a,a,b,c],L).
L = [a, b, c] ;
false.
这是可以的,但请检查以下示例:

remove_duplicates([a,a,b,a,c],L).
L = [b, a, c] ;
L = [b, a, c] ;
false.
这将返回L=[b,a,c],因为它删除了列表的前两个“a”,只保留了第三个

如果只希望删除重复项,由于成员谓词的原因,上述操作将不起作用。 你可以写:

remove_duplicates2([],[]).

remove_duplicates2([H],[H]).

remove_duplicates2([H ,H| T], List) :-remove_duplicates( [H|T], List).

remove_duplicates2([H,Y | T], [H|T1]):- Y \= H,remove_duplicates( [Y|T], T1).
谓词remove_duplicates 2与remove_duplicates类似,但有一些重要区别:

1) 您不使用member,但可以检查当列表[H,H | t]包含两个相同的元素且只保留一个元素时会发生什么,因此忽略第一个H并递归调用remove|u duplicates2([H | t],L)

2) 如果输入了[H,Y | T](至少包含两个元素H,Y和列表尾部的列表)和H\=Y,则调用remove_duplicates2([Y | T],T1),并将H存储到要返回的列表中

3) 最后,因为all子句至少需要两个元素,所以递归的基础是包含一个元素的列表:remove_duplicates2([H],[H])

4) 该子句删除重复项2([],[])。仅当输入为空列表时使用,因此需要覆盖此输入

remove_duplicates2谓词将为您提供:

 remove_duplicates2([a,a,b,a,c],L).
L = [a, b, a, c] ;
false.

remove_duplicates2([a,a,b,c],L).
L = [a, b, c] ;
false.

要删除重复项,如果顺序无关紧要,最简单的方法是使用
sort/2

?- sort([a,a,b,b,c], X).
X = [a, b, c].

?- sort([c,c,a,a,b], X).
X = [a, b, c].
当然,您会看到元素的原始顺序丢失了。更重要的是,只有在您正在排序的列表已经被归零(其中没有自由变量)的情况下,这才保证是正确的。考虑这个小例子:

?- sort([X,Y], [a,a]).
X = Y, Y = a.
如果您的目标是删除重复项,则感觉不完全正确

所以你不妨写下:

must_be(ground, List), sort(List, Unique).
你也可以自己做,并保持原来的秩序。但是,您需要记录到目前为止看到的元素。例如,您可以将它们保留在额外列表中:

到目前为止看到的元素列表在开头是空的

如果到目前为止看到的所有元素都与X不同, 将其放在唯一元素列表中,并将其添加到 到目前为止看到的元素列表

如果到目前为止已看到该元素,请跳过它

这是最直接的方法。还有其他更聪明的方法可能会更复杂,但这是最容易编写的方法。

您可以使用或,如这里的答案所述

% remdup(List, List_No_duplicates).
remdup([],[]).
remdup([H|T],Lnd):- rd1(H,T,T1), remdup(T1,Tnd),Lnd=[H|Tnd].
% rd1(X,L,Lr) removes all occurance of X in List L and store result in List Lr
rd1(_,[],[]).    
rd1(X,[X|T],Lx):-    rd1(X,T,Lx).
rd1(X,[Y|T],Lx):-    rd1(X,T,Lx1),Lx=[Y|Lx1].
如何工作-谓词remdup

  • 取给定列表中的第一个元素H,并删除其在尾部T中的所有引用-将该结果存储在列表T1中;及

  • 在Tnd中的T1存储结果上递归调用remdup;及

  • 将元素H添加回Tnd,它实际上是尾部T,由remdup的递归调用修改,因此已经没有所有重复项,并且不包含任何H的出现

  • 谓词-rd1

  • 从空白列表中删除所有事件和任何内容都是空白列表
  • 如果头部匹配X-则继续在尾部再次调用
  • 如果头部与X不匹配,则继续到尾部;并将不匹配的头部Y添加到修改后的尾部,该尾部现在不再出现X

  • 试试这个,我希望它能帮助你:

    delete3(_,[],[]).
    delete3(X,[X|T],R):- delete3(X,T,R).
    delete3(X,[H|T],[H|R]) :- delete3(X,T,R).
    
    remove_duplicates([],[]).
    remove_duplicates([H|T], [H|R]) :- 
        member(H,T),!,
        delete3(H,T,R1),
        remove_duplicates(R1,R).
    
    remove_duplicates([H|T],[H|R]):-
        remove_duplicates(T,R).
    

    在第二个谓词中,我们验证head变量*(H)*是否是列表*(T)*尾部的成员。如果是,那么我们调用第一个谓词,该谓词删除所有与尾部的head变量相等的变量,依此类推。

    查询
    排序([a,X,b,X],L),X=b如何。
    ?它会导致
    L=[b,a,b]
    ,我认为这是不需要的。@lambda.xy.x是的,即使这样:
    排序([x,Y],[a,a])
    也证明了同样的问题。排序不是逻辑操作,其语义是:“根据术语的标准顺序对第一个参数中的列表进行排序,并将结果与第二个参数统一起来”。您可以查看更多示例。更正了我的答案。@lambda.xy.x的可能副本在[prolog]标签上有许多类似的问题和答案,但是,我不确定提议的(和接受的)解决方案是否一定那么好。我不认为我下面的答案更好,但至少它显示了一种不同的方法。你是对的,有很多不纯的解决方案
    % remdup(List, List_No_duplicates).
    remdup([],[]).
    remdup([H|T],Lnd):- rd1(H,T,T1), remdup(T1,Tnd),Lnd=[H|Tnd].
    % rd1(X,L,Lr) removes all occurance of X in List L and store result in List Lr
    rd1(_,[],[]).    
    rd1(X,[X|T],Lx):-    rd1(X,T,Lx).
    rd1(X,[Y|T],Lx):-    rd1(X,T,Lx1),Lx=[Y|Lx1].
    
    delete3(_,[],[]).
    delete3(X,[X|T],R):- delete3(X,T,R).
    delete3(X,[H|T],[H|R]) :- delete3(X,T,R).
    
    remove_duplicates([],[]).
    remove_duplicates([H|T], [H|R]) :- 
        member(H,T),!,
        delete3(H,T,R1),
        remove_duplicates(R1,R).
    
    remove_duplicates([H|T],[H|R]):-
        remove_duplicates(T,R).