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

时间:2016-09-11 11:43:27

标签: 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不在Tail中时,例如[a,b,b,c,]之类的列表,终端只会说False,因为member(H, T)不是真的。

有什么想法吗?

6 个答案:

答案 0 :(得分:8)

要删除重复项,如果订单无关紧要,最简单的方法是使用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).

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

  

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

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).
  

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

list_unique_2(X, Xs, So_far, [X|Us]) :-
    maplist(dif(X), So_far),
    list_unique_1(Xs, [X|So_far], Us).
  

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

list_unique_2(X, Xs, So_far, Us) :-
    memberchk(X, So_far),
    list_unique_1(Xs, So_far, Us).

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

答案 1 :(得分:3)

你的谓词存在一些问题:

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).

上面检查H是否存在于T中,如果不存在则强制要返回的列表的第一个元素为元素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_duplicates2与remove_duplicates类似,但有一些重要的区别:

1)你没有使用成员,但你检查当你有一个带有两个SAME元素的列表[H,H | T]并且你只保留一个时会发生什么,所以你忽略了第一个H并递归调用remove_duplicates2([H | T],L)。

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

3)最后因为所有子句至少需要两个元素,你的递归基础是带有一个元素的列表: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.

答案 2 :(得分:2)

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

remove_duplicates([], []).

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

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

Introduction to Prolog by Ulle Endriss

中所述

答案 3 :(得分:0)

您可以在此处的答案中使用list_to_set/2或{{1}}。

答案 4 :(得分:0)

% 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

  1. 取给定List的第一个元素H并删除其在Tail部分T中出现的所有内容-将结果存储在List T1中;和

  2. 在Tnd的T1存储结果上递归调用remdup;和

  3. 将元素H添加回Tnd中,该元素实际上是通过remdup的递归调用修改的尾部T,因此已经没有所有重复项,并且不包含任何H。

谓词-rd1

  1. 从空白列表中删除所有出现的内容和所有内容就是空白列表
  2. 如果头部与X匹配-然后继续在尾部再次呼叫
  3. 如果头部与X不匹配-则继续尾部;并将未匹配的头部Y添加到修改后的尾部,该尾部现在不包含所有X

答案 5 :(得分:0)

尝试一下,希望对您有所帮助

@Async
@Service
public class CustomerAsyncService {

    private CustomerRepository customerRepository;

    @Autowired
    public CustomerAsyncService(CustomerRepository customerRepository) {
        this.customerRepository = customerRepository;
    }

    @Transactional(readOnly = true, isolation = Isolation.SERIALIZABLE)
    public ListenableFuture<Page<Customer>> findAll(Pageable pageable) {
        return AsyncResult.forValue(customerRepository.findAll(pageable));
    }

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