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

Mar*_*ijn 8 prolog

所以我需要编写一个谓词remove_duplicates/2来删除给定列表中的重复元素.例如:

?- remove_duplicates([a,a,b,c,c], List). List = [a,b,c] Yes

请记住,我只学习SWI-Prolog两天,只了解Prolog的基础知识.这就是我现在所拥有的:

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

这适用于列表,[a,a,b,c]但不适用于尾部中两个元素相同的列表.我想我不得不将Head删除到临时列表,创建一个新的Head并重复谓词.我不知道该怎么做.此外,当Head不在尾部时,例如列表之类的[a,b,b,c,],终端只是说False,因为member(H, T)不是真的.

有任何想法吗?

小智 9

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

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

?- sort([c,c,a,a,b], X).
X = [a, b, c].
Run Code Online (Sandbox Code Playgroud)

当然,您会看到元素的原始顺序丢失了.更重要的是,如果您正在排序的列表已经基础(其中没有自由变量),这只能保证正确.考虑这个小例子:

?- sort([X,Y], [a,a]).
X = Y, Y = a.
Run Code Online (Sandbox Code Playgroud)

如果您的目标是删除重复项,感觉不完全正确....

所以你不妨写一下:

must_be(ground, List), sort(List, Unique).
Run Code Online (Sandbox Code Playgroud)

您也可以自己完成,并保留原始订单.但是,你需要记录你到目前为止看到的元素.例如,您可以将它们保存在一个额外的列表中:

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

list_unique(List, Unique) :-
    list_unique_1(List, [], Us).

list_unique_1([], _, []).
list_unique_1([X|Xs], So_far, Us) :-
    list_unique_2(X, Xs, So_far, Us).
Run Code Online (Sandbox Code Playgroud)

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

list_unique_2(X, Xs, So_far, [X|Us]) :-
    maplist(dif(X), So_far),
    list_unique_1(Xs, [X|So_far], Us).
Run Code Online (Sandbox Code Playgroud)

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

list_unique_2(X, Xs, So_far, Us) :-
    memberchk(X, So_far),
    list_unique_1(Xs, So_far, Us).
Run Code Online (Sandbox Code Playgroud)

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

  • 查询```sort([a,X,b,X],L),X = b```怎么样?它导致```L = [b,a,b]```我认为这是不需要的. (2认同)

小智 6

一个简单而漂亮的删除重复项的代码是:

remove_duplicates([], []).

remove_duplicates([Head | Tail], Result) :-
    member(Head, Tail), !,
    remove_duplicates(Tail, Result).

remove_duplicates([Head | Tail], [Head | Result]) :-
    remove_duplicates(Tail, Result).

Run Code Online (Sandbox Code Playgroud)

正如Ulle Endriss 的《Prolog 简介》中所述


cod*_*der 5

你的谓词有几个问题:

remove_duplicates([H | T], List) :-
  member(H, T),
  append(T, [], List1).
Run Code Online (Sandbox Code Playgroud)

首先你应该有一个递归谓词,你检查列表的头部,但你不递归地检查列表的尾部:

所以你可以把它改成:

remove_duplicates([H | T], List) :- 
      member(H, T),
      remove_duplicates( T, List).
Run Code Online (Sandbox Code Playgroud)

以上递归调用尾部。

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

remove_duplicates([H | T], [H|T1]) :- 
       \+member(H, T), 
       remove_duplicates( T, T1).
Run Code Online (Sandbox Code Playgroud)

上面检查 H 是否存在于 T 中,如果不存在,则强制您要返回的列表的第一个元素为元素 H:

如果这不是那么清楚,上面等于这个:

  remove_duplicates([H | T], List) :- 
           \+member(H, T), 
           List=[Head|Tail],
           Head=H,
           remove_duplicates( T, Tail).
Run Code Online (Sandbox Code Playgroud)

这不是很优雅,只是为了了解以前的工作原理。

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

remove_duplicates([],[]).
Run Code Online (Sandbox Code Playgroud)

所以根据上述子句的谓词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).
Run Code Online (Sandbox Code Playgroud)

尽管您必须注意上面的每个元素只会保留一次,例如:

remove_duplicates([a,a,b,c],L).
L = [a, b, c] ;
false.
Run Code Online (Sandbox Code Playgroud)

这没问题,但请查看下面的示例:

remove_duplicates([a,a,b,a,c],L).
L = [b, a, c] ;
L = [b, a, c] ;
false.
Run Code Online (Sandbox Code Playgroud)

这将返回 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).
Run Code Online (Sandbox Code Playgroud)

谓词 remove_duplicates2 类似于 remove_duplicates,但有一些重要的区别:

1)您不使用成员,但是您检查当您有一个包含两个相同元素的列表 [H,H|T] 并且只保留一个时会发生什么,因此您忽略第一个 H 并递归调用 remove_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) 子句 remove_duplicates2([],[])。仅在输入为空列表时使用,因此需要覆盖此输入。

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.
Run Code Online (Sandbox Code Playgroud)