List 序言:计算e在奇数位置列表中出现的次数

List 序言:计算e在奇数位置列表中出现的次数,list,prolog,clpfd,List,Prolog,Clpfd,我不明白为什么这总是返回错误。如果我调用另一个函数,我可以在奇数位置计算列表中元素“e”的数量,但我想用一种更干净、更紧凑的方式来计算。代码如下: count(_,[],0). count(E,[E,_|T],C) :- count(E,T,D), C is D+1. count(E,[H,_|T],C) :- E\=H, count(E,T,C). 当列表包含偶数个元素时,您当前的公式是正确的: ?- count(x, [x, y, x, x], C). C

我不明白为什么这总是返回错误。如果我调用另一个函数,我可以在奇数位置计算列表中元素“e”的数量,但我想用一种更干净、更紧凑的方式来计算。代码如下:

count(_,[],0).
count(E,[E,_|T],C) :-
    count(E,T,D),
    C is D+1.
count(E,[H,_|T],C) :-
    E\=H,
    count(E,T,C).

当列表包含偶数个元素时,您当前的公式是正确的:

?- count(x, [x, y, x, x], C).
C = 2 
?- count(x, [x, y, x, x, x], C).
C = 3 

?- count(y, [x, y, x, x, x], C).
C = 0 

?- count(x, [y, y, y, y, x], C).
C = 1 
但如果元素的数量是奇数,它将失败

要更正此问题,您只需为一个元素列表添加两条规则:

count(E,[E],1).
count(E,[H], 0):-
    E\=H.
现在它适用于具有奇数个元素的列表:

?- count(x, [x, y, x, x], C).
C = 2 
?- count(x, [x, y, x, x, x], C).
C = 3 

?- count(y, [x, y, x, x, x], C).
C = 0 

?- count(x, [y, y, y, y, x], C).
C = 1 

@谢尔盖的回答是。但你可能想知道你如何自己发现这样的问题。Prolog实际上可以帮助您完全理解Prolog程序。比其他语言更能解释自己。以下是如何:

使用纯单调的序言 您的程序几乎是一个纯粹、单调的程序。除了
(\=)/2
,它应该被
dif/2
或替换。看看为什么

使用最一般的查询 您抱怨您的程序“不断”返回
false
。真的是这样吗?您考虑过哪些测试用例?有没有办法证明这一点?在传统的、面向价值的编程语言中,答案是您需要借助一些分析器。但在语言中无法证明这一点。也许您很幸运找到了
正确的测试用例
。也许不是

在序言中不是这样!在这里,您可以始终给出一个将为true的查询,前提是有一些测试用例是true 2。你不需要做任何思考(好吧,假装你在思考)。只需为每个参数使用不同的新变量。所以,考虑到你的谓词
count/3
,啊,让我想想,请停止这种嘈杂的打字,我必须理解它(假装假装)。。。。它是:

?- count(E, Xs, M).
例如,如果有任何其他查询成功,它将是此查询的一个实例。因此,它将比这个更具体。由于单调性,更一般的查询也必须是真实的。而且,我们现在得到了一个又一个反例,一个银盘,呃,顶级贝壳上的反例:

| ?- count(E, Xs, N).
Xs = [],
N = 0 ? ;
Xs = [E,_A],
N = 1 ? ;
Xs = [E,_A,E,_B],
N = 2 ? ;
Xs = [E,_A,E,_B,E,_C],
N = 3 ? ;
Xs = [E,_A,E,_B,E,_C,E,_D],
N = 4 ? ...
所以你抱怨你的谓词是假的,但到目前为止我们已经收到了5个正确的答案。这些答案包含无限多个解。要得到所有的解,用你喜欢的基础项替换所有剩余的变量。比如
count(1[1,1],1)
count(hi[hi,u],1)
等等

这些都是答案/解决方案吗?如果我们的计算资源没有耗尽,Prolog会列举所有这些吗?思考如何枚举所有整数:

0、1、2、3-3,-2,-1

嗯,对于我们这些有限的生命来说,它不是这样工作的。我们必须开始了

0,1,-1,2,-2,3,-3

所以有一个无限序列是可以的,但如果我们运气不好,我们可能会在一个后面有一些值

执行公平枚举 但我们并不是运气不好!在某些情况下,我们确实可以得到很好的无限序列。想想:

| ?- length(Xs, L).
Xs = [],
L = 0 ? ;
Xs = [_A],
L = 1 ? ;
Xs = [_A,_B],
L = 2 ? ;
Xs = [_A,_B,_C],
L = 3 ? ;
Xs = [_A,_B,_C,_D],
L = 4 ? ;
Xs = [_A,_B,_C,_D,_E],
L = 5 ? 
现在,结合我们最初的查询:

| ?- length(Xs, L), count(E, Xs, N).
Xs = [],
L = 0,
N = 0 ? ;
Xs = [E,_A],
L = 2,
N = 1 ? ;
Xs = [_A,_B],
L = 2,
N = 0,
prolog:dif(E,_A) ? ;
Xs = [E,_A,E,_B],
L = 4,
N = 2 ? ;
Xs = [E,_A,_B,_C],
L = 4,
N = 1,
prolog:dif(E,_B) ? ...
这些都是答案,按列表的长度排序。一旦我们看到
L
的值增加,我们就知道它下面的所有长度都已经被枚举了

让我们看看
L
的值!它们是0,2,2,4,4,4,6。。。奇数长度在哪里?显然他们失踪了。因此你的程序太专业化了。也许从最小的例子开始

结论性阅读 另一种理解问题的方法是从右到左读取箭头,箭头正好指向
:-
所指的方向

从我们知道的事实来看,
Xs=[]
,从这两条规则中,我们可以得出这样的结论:
Xs=[\uu,\u]
,然后是
Xs=[\uu,\uu,\u]
。因此,仅枚举偶数长度

最后,这里是另一种编写
count/3
的方法:

count(_E, [], 0).
count(E, [E|Xs], N0) :-
   count1(E, Xs, N1),
   N0 is N1+1.
count(E, [X|Xs], N) :-
   dif(E, X),
   count1(E, Xs, N).

count1(_E, [], 0).
count1(E, [_|Xs], N) :-
  count(E, Xs, N).
啊,我忘了提到
库(clpfd)
,它也可以使用。看


精细印刷 1很容易发现面向值的编程语言:它们的变量在运行时总是值

2某些限制适用于:
事实上,这只是纯单调程序中的情况,这就是为什么我首先坚持这个性质。然后,结果可能会被非终止或错误所掩盖。

如果您的Prolog系统支持,您可以使用以下命令 计数的定义/3。这里显示的代码既高效(避免创建无用的选择点),又逻辑纯

count(E,Xs,C) :-
   C #>= 0,
   list_item_count(Xs,E,C).

list_item_count([],_,0).
list_item_count([X,_|Xs],E,C) :-
   if_(E=X, C0+1 #= C, C0 = C),
   list_item_count(Xs,E,C0).
继续讨论一些问题:

?- count(1,[1,2,3,1,2],C).            % fails: list has wrong length
false.

?- count(1,[1,2,3,1,2,1],C).
C = 1.                                % succeeds deterministically

?- count(E,[1,2,3,1,2,1],C).          % now some more general query!
E = C, C = 1 ;                        % this must be non-deterministic ...    
E = 3, C = 1 ;                        % ... lest we lose solutions!
E = 2, C = 1 ;
C = 0, dif(E,2), dif(E,3), dif(E,1).

?- length(Xs,_),count(X,Xs,C).        % even more general; fair enumeration
C = 0, Xs = []                                      ;
C = 1, Xs = [X,_A]                                  ;
C = 0, Xs = [_A,_B],            dif(X,_A)           ;
C = 2, Xs = [X,_A,X,_B]                             ;
C = 1, Xs = [X,__A,_B,_C],      dif(X,_B)           ;
C = 1, Xs = [_A,_B, X,_C],      dif(X,_A)           ;
C = 0, Xs = [_A,_B,_C,_D],      dif(X,_A),dif(X,_B) ;
C = 3, Xs = [X,_A, X,_B, X,_C]                      ;
C = 2, Xs = [X,_A, X,_B,_C,_D], dif(X,_C)           ... 

谢谢我觉得第一个谓词是必要的,我想我能理解为什么第二个谓词是必要的。