一个简单的求和程序
xxxxxxxxxx
71sum_to(1, 1).
2sum_to(N, R) :-
3N1 is N - 1,
4sum_to(N1, R1),
5R is N + R1.
6
7?- sum_to(3,X).
运行结果是
xxxxxxxxxx
151?- sum_to(3,X).
2X = 6 ;
3ERROR: Stack limit (1.0Gb) exceeded
4ERROR: Stack sizes: local: 1.0Gb, global: 30Kb, trail: 1Kb
5ERROR: Stack depth: 11,183,606, last-call: 0%, Choice points: 3
6ERROR: 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 or
14ERROR: ?- 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)技术,将程序改为
xxxxxxxxxx
71sum_to(1, 1) :- !.
2sum_to(N, R) :-
3N1 is N - 1,
4sum_to(N1, R1),
5R is N + R1.
6
7?- sum_to(3,X).
剪切技术体现在"!"上, 相应的运行结果是:
xxxxxxxxxx
41?- sum_to(3,X).
2X = 6.
3
4?-
如果不用剪切技术, 还有另外一种解决方案,就是加入一句"\+(N = 1)":
xxxxxxxxxx
61sum_to(1, 1).
2sum_to(N, R) :-
3\+(N = 1),
4N1 is N - 1,
5sum_to(N1, R1),
6R is N + R1.
运行结果是:
xxxxxxxxxx
51?- sum_to(3,X).
2X = 6 ;
3false.
4
5?-
一个飞鸟的例子
xxxxxxxxxx
151bird(sparrow).
2bird(eagle).
3bird(duck).
4bird(crow).
5bird(ostrich).
6bird(puffin).
7bird(swan).
8bird(albatross).
9bird(starling).
10bird(owl).
11bird(kingfisher).
12bird(thrush).
13
14can_fly(ostrich):- fail.
15can_fly(X):-bird(X).
xxxxxxxxxx
21?- can_fly(ostrich).
2true.
要纠正这个错误,可以使用cut with failure组合,改为
xxxxxxxxxx
21can_fly(ostrich):- !,fail.
2can_fly(X):-bird(X).
再运行就对了
xxxxxxxxxx
21?- can_fly(ostrich).
2false.
考虑以下两种方法实现阶乘:
xxxxxxxxxx
51fact_simple(0, 1):- !.
2fact_simple(N, F) :-
3N1 is N-1,
4fact_simple(N1, F1),
5F is N*F1.
xxxxxxxxxx
21?- fact_simple(16, F).
2F = 720.
Prolog系统需要维持一个不断增长的堆栈。
使用加速器(Accumulator),避免维护堆栈。
xxxxxxxxxx
61fact_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).
xxxxxxxxxx
21?- fact_acc(6, F).
2F = 720.
无加速器版的实现不断调用自己完成计算,而有加速器版的实现使用Acc作为加速变量(中间变量).
计算Fibonacci数列
xxxxxxxxxx
81fib(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.
xxxxxxxxxx
21?- fib(38,X).
2X = 39088169.
xxxxxxxxxx
81fibo(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).
xxxxxxxxxx
21?- fibo(38,X).
2X = 39088169.
加速效果在N=38的时候已经很明显,不用加速器运行时间很长,而使用加速器非常快。