我是 Prolog 的新手,我自己似乎无法得到答案。
我想要的是,Prolog 计算列表中的数字,而不是每个元素。例如:
getnumbers([1, 2, c, h, 4], X).
应该给我:
X=3
getnumbers([], 0).
getnumbers([_ | T], N) :- getnumbers(T, N1), N is N1+1.
是我所拥有的,但它显然给了我列表中的每个元素。我不知道如何以及在哪里放置“仅计数”。
像往常一样,当您使用列表(和 SWI-Prolog)时,您可以使用在那里找到的模块lambda.pl:http://www.complang.tuwien.ac.at/ulrich/Prolog-inedit/lambda.pl
:- use_module(library(lambda)).
getnumbers(L, N) :-
foldl(\X^Y^Z^(number(X)
-> Z is Y+1
; Z = Y),
L, 0, N).
考虑使用内置谓词(例如在 SWI-Prolog 中),如果您对如何自己做感兴趣,请检查它们的实现:
include(number, List, Ns), length(Ns, N)
保持逻辑纯洁,这很容易:将元谓词
tcount/3
与具体的类型测试谓词number_t/2
(缩写number_truth/2
)一起使用:
number_t(X,Truth) :- number(X), !, Truth = true.
number_t(X,Truth) :- nonvar(X), !, Truth = false.
number_t(X,true) :- freeze(X, number(X)).
number_t(X,false) :- freeze(X,\+number(X)).
让我们运行 OP 建议的查询:
?- tcount(number_t,[ 1 , 2 ,c,h, 4 ],N)。 N = 3. % 确定性成功
请注意,这是单调的:延迟变量绑定在逻辑上总是合理的。考虑:
?- tcount(number_t,[A,B,C,D,E],N), A=1 , B=2 , C=c, D=h, E=4。 N = 3,A = 1,B = 2,C = c,D = h,E = 4;% 成功,但留下选择点 错误的。
最后,让我们看一下以下相当普遍的查询的一些答案:
?- tcount(number_t,[A,B,C],N).
N = 3, freeze(A, number(A)), freeze(B, number(B)), freeze(C, number(C)) ;
N = 2, freeze(A, number(A)), freeze(B, number(B)), freeze(C,\+number(C)) ;
N = 2, freeze(A, number(A)), freeze(B,\+number(B)), freeze(C, number(C)) ;
N = 1, freeze(A, number(A)), freeze(B,\+number(B)), freeze(C,\+number(C)) ;
N = 2, freeze(A,\+number(A)), freeze(B, number(B)), freeze(C, number(C)) ;
N = 1, freeze(A,\+number(A)), freeze(B, number(B)), freeze(C,\+number(C)) ;
N = 1, freeze(A,\+number(A)), freeze(B,\+number(B)), freeze(C, number(C)) ;
N = 0, freeze(A,\+number(A)), freeze(B,\+number(B)), freeze(C,\+number(C)).
这使用 Prolog 与 number/1 的自然模式匹配,以及一个附加子句(下面的 3)来处理不是数字的情况。
% 1 - base recursion
getnumbers([], 0).
% 2 - will pass ONLY if H is a number
getnumbers([H | T], N) :-
number(H),
getnumbers(T, N1),
N is N1+1.
% 3 - if got here, H CANNOT be a number, ignore head, N is unchanged, recurse tail
getnumbers([_ | T], N) :-
getnumbers(T, N).
此类问题的一个常见 prolog 习语是首先定义您的谓词以供公共消费,并让它调用一个“工人”谓词。通常它会使用某种累加器。对于您的问题,公共消费谓词类似于:
count_numbers( Xs , N ) :-
count_numbers_in_list( Xs , 0 , N ) .
count_numbers_in_list( [] , N , N ) .
count_numbers_in_list( [X|Xs] , T , N ) :-
number(X) ,
T1 is T+1 ,
count_numbers_in_list( Xs , T1 , N )
.
您需要构造递归位,使其也是尾递归的,这意味着递归调用只依赖于参数列表中的数据。这允许编译器在每次调用时重用现有的堆栈帧,因此谓词实际上变成了迭代而不是递归。一个适当的尾递归谓词可以处理无限长的列表;一个不是将在每次递归时分配一个新的堆栈帧并最终破坏它的堆栈。以上count_numbers_in_list/3
是尾递归。这不是:
getnumbers([H | T], N) :-
number(H),
getnumbers(T, N1),
N is N1+1.