第一段代码:
count(X, [], 0).
count(X, [X|T], N) :-
count(X, T, N1),
N is N1 + 1.
count(X, [Y|T], N) :-
X \= Y,
count(X, T, N).
它显示列表中给定元素的重复次数。 (没有问题!)例如,
?- count(5, [1,5,5,3,3,3,4,2], X).
X = 2.
第二段代码:
found(0, [], X).
found(N, [H|T], R):-
count(R, [R|T], C),
C == N.
found(N, [H|T], R):-
count(R, [R|T], C),
C \= N,
found(N, T, R).
通过给出列表中的重复次数来显示元素。 (但它并不完美)。例如,
?- found(2, [1,5,5,3,3,3,4], X).
X = 5.
这没关系。然而,
?- found(3, [1,5,5,3,3,3,4], X).
X = 5.
这是错的!我不知道如何解决它。
答案 0 :(得分:1)
首先要做的事情! count/3
和found/3
相同。
我们使用clpfd,meta-predicate tcount/3
和明确的术语平等(=)/3
来定义count/3
:
:- use_module(library(clpfd)).
count(E,Xs,N) :-
tcount(=(E),Xs,N).
问:“N
中e
的出现次数[a,e,e,c,c,c,d,b]
是多少?”
?- count(e,[a,e,e,c,c,c,d,b],N). N = 2.
问:“X
中哪个[a,e,e,c,c,c,d,b]
>发生三次以上?”
?- N #> 3, count(X,[a,e,e,c,c,c,d,b],N). false.
问:“X
中哪个[a,e,e,c,c,c,d,b]
正好发生三次?”
?- count(X,[a,e,e,c,c,c,d,b],3). X = c ; false.
问:“X
中哪个[a,e,e,c,c,c,d,b]
发生正好两次?”
?- count(X,[a,e,e,c,c,c,d,b],2). X = e ; false.
问:“X
中哪个[a,e,e,c,c,c,d,b]
只发生一次?”
?- count(X,[a,e,e,c,c,c,d,b],1). X = a ; X = d ; X = b ; false.
问:“X
中哪个[a,e,e,c,c,c,d,b]
完全为零?
?- count(X,[a,e,e,c,c,c,d,b],0). dif(X,a), dif(X,b), dif(X,c), dif(X,d), dif(X,e).
答案 1 :(得分:0)
你说count/3
没有问题,但我不同意:
?- count(X, [1,5,5,3,3,3,4,2], Z).
X = Z, Z = 1 ;
false.
我想在这里看到的是:
X = Z, Z = 1 ;
X = 5, Z = 2 ;
X = Z, Z = 3 ;
X = 4, Z = 1 ;
X = 2, Z = 1 ;
false.
在我看来count/3
应该能够列出第一个以外的列表项。事实上,它只能提出,因为第二条规则对列表的头部有所说明。由于第一条规则,您的单身警告会出现,这可以更好地说明:
count(_, [], 0).
您对该规则的真实含义是,"无论您要查找什么,如果列表为空,则该项的计数为0。"我可以想出其他方法来重述为第一个参数生成的谓词,但是没有一个方法可以保留这个非常令人愉悦的属性。我怀疑@false会在某个时刻出现并告诉我们两个为什么我们都错了。但这就是我倾向于陈述count/3
的方式:
count(Item, List, 0) :- \+ memberchk(Item, List).
count(Item, List, N) :-
% get us each unique item, one by one (or confirm that X is in the list)
sort(List, Sorted), member(Item, Sorted),
% make a list of the items that match
bagof(Item, member(Item, List), Occurrences),
% count them
length(Occurrences, N).
这很奇怪而且很尴尬,可能有更好的方法,但它符合我们的要求:
?- count(X, [1,5,5,3,3,3,4,2], Z).
X = Z, Z = 1 ;
X = 2,
Z = 1 ;
X = Z, Z = 3 ;
X = 4,
Z = 1 ;
X = 5,
Z = 2.
?- count(0, [1,5,5,3,3,3,4,2], Z).
Z = 0 ;
false.
?- count(5, [1,5,5,3,3,3,4,2], Z).
Z = 2.
现在让我们把注意力转向found/3
。首先让我们更改代码以解决单例错误,以便我们可以看到问题所在:
found(0, [], _).
found(N, [_|T], R):-
count(R, [R|T], C),
C == N.
found(N, [_|T], R):-
count(R, [R|T], C),
C \= N,
found(N, T, R).
我们立即看到,在第二和第三条规则中,我们在列表的前面暗示了一个R.可能在这两种情况下我们都希望保留H并传递它或做一些棘手的事情。事实上,使用L可能更安全,因为我们不应该对列表做任何奇特的事情 - count/3
过程应该足够了。在检查时,我不知道你试图用第三条规则完成什么。事实上,我们可以完全删除它,我们得出以下定义:
found(0, [], _).
found(N, L, R):-
count(R, L, C),
C == N.
它适用于您的两个用例,但猜猜是什么?我们根本不需要found/3
,因为count/3
现在可以做到了!
?- count(X, [1,5,5,3,3,3,4], 3).
X = 3 ;
false.
?- count(X, [1,5,5,3,3,3,4], 2).
X = 5.
这些实际上是在问问题" 2次出现的元素是什么?" Prolog消除了对整个其他谓词的需求。您现在可以通过以下简单的定义轻松确定它:
found(X, Y, Z) :- count(Z, Y, X).
一个分手评论:如果您使用真实的非单字母变量名称,您可能会发现您的代码更容易调试。看看我对count/3
的定义。它有点大,但更容易理解。我经常在自己的代码中使用[H|T]
或[X|Xs]
,但一般来说,X,Y,Z等的扩散使得在阅读代码时很难遵循,并且阅读代码是在声明性语言中非常重要,其中大部分流程实际上是关于在哪里使用哪些变量。