Prolog no_duplicate函数

时间:2014-09-25 20:06:58

标签: prolog prolog-dif

我试图编写一个简单的程序来检查列表是否有重复项。这是我到目前为止所尝试的:

% returns true if the list has no duplicate items.
no_duplicates([X|XS]) :- member(X,XS) -> false ; no_duplicates(XS).
no_duplicates([]) :- true. 

如果我尝试no_duplicates([1,2,3,3])。它说的是真的。为什么是这样?我可能在这里误解了Prolog,但感谢任何帮助。

5 个答案:

答案 0 :(得分:7)

回答您的问题:您的解决方案实际上无法按预期no_duplicates([1,2,3,3])失败。所以没有问题。

现在进行查询:

?- A = 1, no_duplicates([A, 2]).
A = 1.
?-        no_duplicates([A, 2]), A = 1.

它们都意味着相同,所以我们应该期待Prolog会产生相同的答案。 (更确切地说,我们期望相同的忽略错误和非终止)。

然而,四个提议的解决方案不同!而那个没有的,不同的是:

?- A = 2, no_duplicates([A, 2]).
false.
?-        no_duplicates([A, 2]), A = 2.

请注意,它始终是第二个产生麻烦的查询。要解决这个问题,我们需要no_duplicates([A, 2])的答案。它不能是false,因为A有一些值可以使其成立。像A = 1一样。它也不是真的,因为有些值不适合,例如A = 2

另一种可能性是在这种情况下发出instantiation_error。含义:我没有足够的信息,所以我最好停下来,而不是弄乱可能不正确的信息。

理想情况下,我们会得到一个涵盖所有可能解决方案的答案。这个答案是dif(A, 2),这意味着与2不同的所有A都是解决方案。

dif/2是最古老的内置谓词之一,Prolog 0确实拥有它。不幸的是,后来的发展在Prolog I和Edinburgh Prolog以及ISO Prolog中丢弃了它。

然而,包括SICStus,YAP,SWI在内的现有系统都提供它。在ISO-Prolog

中有一种安全的approximate dif/2 safely方式
no_duplicates(Xs) :-
   all_different(Xs). % the common name

all_different([]).
all_different([X|Xs]) :-
   maplist(dif(X),Xs).
   all_different(Xs).

请参阅:

答案 1 :(得分:2)

列表中的重复项是列表中不在同一位置的相同元素,因此可以写入no_duplicates:

no_duplicates(L) :-
    \+((nth0(Id1, L, V), nth0(Id2, L, V), Id1 \= Id2)).

答案 2 :(得分:2)

我会更具描述性地解决问题:

no_duplicates( []     ) .  % the empty list is unique
no_duplicates( [X|Xs] ) :- % a list of length 1+ is unique
  \+ member(X,Xs) ,        % - if its head is not found in the tail,
  no_duplicates(Xs)        % - and its tail is itself unique.
  .                        %

考虑到这一点,因为这是一个有点昂贵的操作 - O(n 2 )? - 使用sort/2可能更有效,并利用它生成有序集,删除重复的事实。你可以说像

no_duplicates( L ) :-
  sort(L,R)     % sort the source list, removing duplicates
  length(L,N) , % determine the length of the source list
  Length(R,N) , % check that against the result list
  .

或者您可以使用msort/3(不会删除重复项),也可能会更快一点:

no_duplicates( L ) :-
  msort(L,R),            % order the list
  \+ append(_,[X,X|_],R) % see if we can find two consecutive identical members
  .

答案 3 :(得分:2)

这是另一种方法,可行,因为sort/2删除了重复项:

no_duplicates(L) :-
    length(L, N),
    sort(L, LS),
    length(LS, N).

答案 4 :(得分:1)

杰已经注意到你的代码正在运行。另一种,略显冗长的

no_duplicates(L) :- \+ (append(_, [X|XS], L), memberchk(X, XS)).