prolog查找列表的基数

prolog查找列表的基数,prolog,Prolog,我已经编写了一个Prolog代码来查找列表的基数,即不同元素的数量。它给出了正确的输出,但它运行了多次,我似乎不能让我的头围绕它。我用过调试器,但不明白出了什么问题 member(A, [A|_]). member(A, [_|L]) :- member(A, L). crdnlty([],0). crdnlty([A|R],N) :- ( \+ member(A, R), crdnlty(R, N1), N is N1+1 ); (

我已经编写了一个Prolog代码来查找列表的基数,即不同元素的数量。它给出了正确的输出,但它运行了多次,我似乎不能让我的头围绕它。我用过调试器,但不明白出了什么问题

member(A, [A|_]).
member(A, [_|L]) :- member(A, L).

crdnlty([],0).
crdnlty([A|R],N) :-
    (
      \+ member(A, R),
      crdnlty(R, N1),
      N is N1+1
    );
    (
      member(A, R),
      crdnlty(R, N)
    ).
成员检查剩余列表中是否存在
如果不存在,即该元素的最后一次出现,则基数增加1

例如,如果我运行查询

crdnlty([1,2,1,1], N).
它回来了

N = 2 ;
N = 2 ;
false.
但它应该会回来

N = 2 ;
false.

最好构建一个不同元素的列表,并为基数指定其长度:

crdnlty([A|R],N) :- distinct(R,N,[A],1).

% distinct(L,N,DL,DN): There are N distinct values in list L+DL,
% assuming there are DN distinct values in list DL alone.
distinct([],N,_,N).
distinct([A|R],N,DL,DN) :- 
    (
      \+ member(A, DL),
      DN1 is DN+1,
      distinct(R, N, [A|DL], DN1)
    );
    (
      member(A, DL),
      distinct(R, N, DL, DN)
    ).

这不是答案,只是一个不适合评论的测试建议。

除了不需要的重复解决方案之外,还有一个关于如何测试谓词的问题。一个简单的替代解决方案是使用ISO Prolog标准谓词
sort/2
和事实上的标准谓词
length/2
。替代解决办法可以是:

cardinality(List, Cardinality) :-
    sort(List, Sorted),
    length(Sorted, Cardinality).
我们可以使用此替代解决方案定义一个属性,您的解决方案必须遵守该属性,该属性允许快速检查您的解决方案(暂时忽略不需要的非确定性):

使用Logtalk的
lgtunit
工具提供的快速检查实现(您可以在大多数Prolog系统中运行;在本例中,我将使用GNU Prolog):

当然,QuickCheck可以显示bug,但不能证明它们不存在。这就是说,Logtalk的QuickCheck实现的一个显著特征是,它在生成随机值之前尝试指定类型的琐碎/角落情况。这有助于确保随机测试不会遗漏明显的测试用例(如我们接下来所示)

如果我们测试Scott Hunter提供的解决方案,会发生什么

| ?- lgtunit::quick_check(property(+list(integer)), [n(2000)]).      
*     quick check test failure (at test 1 after 0 shrinks):
*       property([])

no
事实上,他的解决方案没有考虑到列表可能是空的。假设这被认为是一个bug,添加缺少的子句:

crdnlty([], 0).
重新测试:

| ?- lgtunit::quick_check(property(+list(integer)), [n(2000)]). 
% 2000 random tests passed

(1509 ms) yes

您是否尝试过逐步执行这个过程,以了解为什么它会提出两种方法来推导证明?我已经逐步执行了这个过程,并意识到在A=1和R=[]的情况下,多个规则都适合这个情况,那么Prolog是否会同时适合这两种情况并给出从这两种情况推导出的答案@Scotthunter给你投票,因为这似乎是这里的社会规范。不要投好的答案,但要投好答案中的评论。
crdnlty([], 0).
| ?- lgtunit::quick_check(property(+list(integer)), [n(2000)]). 
% 2000 random tests passed

(1509 ms) yes