一个简单的求和程序
xxxxxxxxxx71sum_to(1, 1).2sum_to(N, R) :-3N1 is N - 1,4sum_to(N1, R1),5R is N + R1.67?- sum_to(3,X).
运行结果是
xxxxxxxxxx151?- sum_to(3,X).2X = 6 ;3ERROR: Stack limit (1.0Gb) exceeded4ERROR: Stack sizes: local: 1.0Gb, global: 30Kb, trail: 1Kb5ERROR: Stack depth: 11,183,606, last-call: 0%, Choice points: 36ERROR: In:7ERROR: [11,183,606] user:sum_to(-11183593, _7882)8ERROR: [11,183,605] user:sum_to(-11183592, _7902)9ERROR: [11,183,604] user:sum_to(-11183591, _7922)10ERROR: [11,183,603] user:sum_to(-11183590, _7942)11ERROR: [11,183,602] user:sum_to(-11183589, _7962)12ERROR:13ERROR: Use the --stack_limit=size[KMG] command line option or14ERROR: ?- set_prolog_flag(stack_limit, 2_147_483_648). to double the limit.15?-
原因是当运行到sum_to(1, R)的时候,尽管sum_to(1, 1)已经提供解,但是sum_to(N, R)的第二个定义还会运行,称为回溯(Backtracking)。
糟糕的是:这个运行是无法终止的。
解决前面的问题可以用剪切(Cut)技术,将程序改为
xxxxxxxxxx71sum_to(1, 1) :- !.2sum_to(N, R) :-3N1 is N - 1,4sum_to(N1, R1),5R is N + R1.67?- sum_to(3,X).
剪切技术体现在"!"上, 相应的运行结果是:
xxxxxxxxxx41?- sum_to(3,X).2X = 6.34?-
如果不用剪切技术, 还有另外一种解决方案,就是加入一句"\+(N = 1)":
xxxxxxxxxx61sum_to(1, 1).2sum_to(N, R) :-3\+(N = 1),4N1 is N - 1,5sum_to(N1, R1),6R is N + R1.
运行结果是:
xxxxxxxxxx51?- sum_to(3,X).2X = 6 ;3false.45?-
一个飞鸟的例子
xxxxxxxxxx151bird(sparrow).2bird(eagle).3bird(duck).4bird(crow).5bird(ostrich).6bird(puffin).7bird(swan).8bird(albatross).9bird(starling).10bird(owl).11bird(kingfisher).12bird(thrush).1314can_fly(ostrich):- fail.15can_fly(X):-bird(X).
xxxxxxxxxx21?- can_fly(ostrich).2true.
要纠正这个错误,可以使用cut with failure组合,改为
xxxxxxxxxx21can_fly(ostrich):- !,fail.2can_fly(X):-bird(X).
再运行就对了
xxxxxxxxxx21?- can_fly(ostrich).2false.
考虑以下两种方法实现阶乘:
xxxxxxxxxx51fact_simple(0, 1):- !.2fact_simple(N, F) :-3N1 is N-1,4fact_simple(N1, F1),5F is N*F1.
xxxxxxxxxx21?- fact_simple(16, F).2F = 720.
Prolog系统需要维持一个不断增长的堆栈。
使用加速器(Accumulator),避免维护堆栈。
xxxxxxxxxx61fact_acc(N, F) :- fact_acc(N, 1, F).2fact_acc(0, Acc, Acc):- !.3fact_acc(N, Acc0, F) :-4N1 is N-1,5Acc is Acc0 * N,6fact_acc(N1, Acc, F).
xxxxxxxxxx21?- fact_acc(6, F).2F = 720.
无加速器版的实现不断调用自己完成计算,而有加速器版的实现使用Acc作为加速变量(中间变量).
计算Fibonacci数列
xxxxxxxxxx81fib(1,1):- !.2fib(2,1):- !.3fib(S,N) :-4A is S-1,5B is S-2,6fib(A,C),7fib(B,D),8N is C+D.
xxxxxxxxxx21?- fib(38,X).2X = 39088169.
xxxxxxxxxx81fibo(N,F):-2N > 0,3fibacc(N,0,1,F).4fibacc(1,_,F,F):- !.5fibacc(N,A1,A2,F) :-6N1 is N-1,7Acc is A1+A2,8fibacc(N1,A2,Acc,F).
xxxxxxxxxx21?- fibo(38,X).2X = 39088169.
加速效果在N=38的时候已经很明显,不用加速器运行时间很长,而使用加速器非常快。