1

我正在学习序言,我想计算列表中特定元素的出现次数。

所以这里是代码 -

count(_, [], _) := !.

count(El, [El|T], N) :-
    N1 is N + 1,
    count(El, T, N1).

count(El, [_|T], N) :-
    count(El, T, N).

check(List1, List2) :-
    count(3, List1, M),
    count(2, List2, N),
    M is N.

所以基本上我想传递给控制台检查([3,4,3],[2,3,4,5,2]),它应该返回 true,因为 list1 中 3 的出现与清单 2 中的 2。但相反,它让我 -

Arguments are not sufficiently instantiated. 
Exception: (10) _G1383 is _L155+1 ? creep
Exception: (9) count(3, [3, 4, 2], _G1385) ? creep
Exception: (8) count(3, [2, 3, 4, 2], _G1385) ? creep
Exception: (7) check([2, 3, 4, 2], [2, 3, 4]) ? creep 

这是什么原因,我该如何解决?我检查了所有论坛,并且到处都写着这应该可以工作。这是某种与版本相关的东西,还是我真的在这里遗漏了什么?

编辑:使用SWI-Prolog

编辑2:

搞定了,谢谢!

代码:

count(_, [], 0) :- !.

count(El, [El|T], N) :-
    count(El, T, N1),
    N #= N1 + 1.

count(El, [Y|T], N) :-
    El \= Y,
    count(El, T, N).

check(List1, List2) :-
    count(3, List1, M),
    count(2, List2, N),
    M #= N.
4

2 回答 2

2

通过使用声明性算术,您现在可以使用它了,真是太好了!

我对您获得的解决方案有一些额外的评论,即:

计数(_,[],0):-!。

计数(埃尔,[埃尔|T],N):-
    计数(El,T,N1),
    N #= N1 + 1。

计数(El,[Y|T],N):-
    埃尔\= Y,
    计数(El,T,N)。

检查(列表 1,列表 2):-
    计数(3,列表1,M),
    计数(2,列表2,N),
    M#=N。

首先,请注意check/2代码中没有使用它,因此我在下面省略了它的定义。

最一般的查询

当我查看我一无所知的 Prolog 代码时,我总是首先尝试最通用的查询,其中所有参数都是变量。理想情况下,答案向我展示了一般的解决方案。

例如,在您的情况下:

?- 计数(E,Ls,C)。
LS = [],
C = 0

这——错误地——表明你的谓词只有一个!这显然不是故意的。

因此,作为第一步,我建议您删除!/0并将此代码更改为:

计数(_,[],0)。

计数(埃尔,[埃尔|T],N):-
    计数(El,T,N1),
    N #= N1 + 1。

计数(El,[Y|T],N):-
    埃尔\= Y,
    计数(El,T,N)。

应用此更改后,我们得到:

?- 计数(E,Ls,C)。
LS = [],
C = 0 ;
Ls = [E],
C = 1 ;
Ls = [E, E],
C = 2;
Ls = [E, E, E],
C = 3

这看起来好多了:我们现在得到了几个有效的答案

终止

这个谓词可能产生多少答案?特别是,是否只有有限多个答案?如果是这样,我们希望以下查询终止

?- 计数(E,Ls,C),

您可以尝试一下,看看它实际上并没有终止(至少不会很快终止)。这是一个好兆头,因为从 的正确实现中count/3,我们期望在最一般的情况下不会终止!

完整性

理想情况下,我们希望谓词是完整的:它不应该省略有效的答案。

例如:

?- 计数(E,[X,Y,Z],C)。
E = X,X = Y,Y = Z,
C = 3 ;
错误的。

这些真的都是解决方案吗?我不这么认为!当然有长度为 3 的列表不同于 [E,E,E].

而且,事实上,您的程序在某种意义上也“知道”它们:

?- 计数(E,[1,2,3],C)。
E = C,C = 1;
错误的。

但同样,这些肯定不是所有情况与 1不同的答案在E哪里?

您正面临这些问题,因为您使用的是非单调 (\=)/2 谓词。这有一些非常难以理解的属性,特别是如果您目前才刚刚开始学习 Prolog。例如:

?- X \= Y, XY = ab。
的。

?- XY = ab, X \= Y。
X = 一个,
Y = b

我建议使用dif/2来表示两个术语是不同的,获得以下版本:

计数(_,[],0)。

计数(埃尔,[埃尔|T],N):-
    计数(El,T,N1),
    N #= N1 + 1。

计数(El,[Y|T],N):-
    差异(El,Y),
    计数(El,T,N)。

有了这个版本,我们得到:

?- 计数(E,[X,Y,Z],C)。
E = X,X = Y,Y = Z,
C = 3 ;
E = X,X = Y,
C = 2,
差异(Y,Z);
E = X,X = Z,
C = 2,
差异(Z,Y);
等等

而且,特别是:

?- 计数(E,[1,2,3],C)。
E = C,C = 1;
E = 2,
C = 1 ;
E = 3,
C = 1 ;
C = 0,
差异(E,3),
差异(E,2),
差异(E,1)

这涵盖了所有可能出现的情况!

公平枚举

由于谓词是单调的,我们可以用它通过迭代深化来公平地列举答案。

例如:

?- 长度(Ls,_),计数(E,Ls,C)。
LS = [],
C = 0 ;
Ls = [E],
C = 1 ;
LS = [_G588],
C = 0,
差异(E,_G588);
Ls = [E, E],
C = 2;
Ls = [E, _G597],
C = 1,
差异(E,_G597)。
C = 2 ;
等等

这非常好,并且表明我们可以将其用作真正的关系,不仅用于计数,还用于生成

因此,您可以考虑更恰当地描述该谓词的一般含义的谓词名称。我把它留作练习。

尾递归版本

请注意,由于您使用的是谓词,因此您可以自由地重新排序您的目标,并使您的谓词tail recursive产生:

计数(_,[],0)。
计数(埃尔,[埃尔|T],N):-
    N #= N1 + 1,
    计数(El,T,N1)。
计数(El,[Y|T],N):-
    差异(El,Y),
    计数(El,T,N)。

决定论

我们目前还有例如:

?- 计数(a,[a,a,a],Cs)。
Cs = 3 ; 
的。

使用if_/3,您可以提高此谓词的确定性:

:- 使用模块(库(reif))。

计数(_,[],0)。
计数(E,[L|Ls],N):-
        if_ (E=L, N #= N1 + 1, N #= N1),
        计数(E,Ls,N1)。

这使您的谓词至少适合索引。在这种情况下是否提高确定性取决于 Prolog 系统的索引机制。

于 2016-11-19T09:20:00.100 回答
1

您正在使用称为模式的谓词,因为它们只能在非常特定的情况下使用。特别是,(is)/2不能用作您在此处需要的关系。

解决此问题的一种方法是改用更通用的谓词。例如,在对整数进行推理时,请考虑使用 Prolog 系统的CLP(FD) 约束,它适用于所有方向。

例如,使用 GNU Prolog,只要简单地替换 (is)/2为:就可以消除错误(#=)/2

数数(_, [], _)。

计数(埃尔,[埃尔|T],N):-
    N1 #= N + 1,
    计数(El,T,N1)。

计数(El,[_|T],N):-
    计数(El,T,N)。

检查(列表 1,列表 2):-
    计数(3,列表1,M),
    计数(2,列表2,N),
    M#=N。

现在我们得到:

?- count(3, [3, 4, 2], C)。
C+1#=_1204 ;
真的 ;
假的。

(或者,取决于您的 Prolog 系统,一个等效的答案)。

为什么?显然,这个程序有点缺陷。

我把寻找错误作为一个练习。提示:M #= N看起来很可疑:这是真的,当且仅 M等于 N...

于 2016-11-17T12:03:06.400 回答