Recursion 为什么递归函数在调用外部谓词时会失败,从而使其成功?
我在写一个阶乘函数的实现。但我不太清楚我的方法失败的原因:Recursion 为什么递归函数在调用外部谓词时会失败,从而使其成功?,recursion,prolog,Recursion,Prolog,我在写一个阶乘函数的实现。但我不太清楚我的方法失败的原因: factorial(0,1). factorial(N,F) :- X is div(F,N), factorial(N-1,X). 我给它一个测试用例: ?- factorial(1,1). true 但是它给了我一个0除的错误。当这个函数达到这个值时,为什么它不回到析因(0,1)的定义,我怎样才能确保它是这样的呢?N-1是一个术语而不是一个数字 您希望定义 factorial(N,F) :- X is d
factorial(0,1).
factorial(N,F) :-
X is div(F,N),
factorial(N-1,X).
我给它一个测试用例:
?- factorial(1,1).
true
但是它给了我一个0除的错误。当这个函数达到这个值时,为什么它不回到析因(0,1)的定义,我怎样才能确保它是这样的呢?
N-1
是一个术语而不是一个数字
您希望定义
factorial(N,F) :-
X is div(F,N),
N1 is N - 1,
factorial(N1,X).
如果跟踪原始代码的执行情况,您会立即看到这一点
?- trace,factorial(1,1),notrace.
Call: (8) factorial(1, 1) ? creep
Call: (9) _G1063 is 1 div 1 ? creep
Exit: (9) 1 is 1 div 1 ? creep
Call: (9) factorial(1-1, 1) ? creep
Call: (10) _G1069 is 1 div (1-1) ? creep
ERROR: div/2: Arithmetic: evaluation error: `zero_divisor'
术语1-1
与0
不统一,因此解释者求助于第二个从句
如果N1为N-1
,则跟踪看起来与您预期的一样
?- trace,factorial(1,1),notrace.
Call: (8) factorial(1, 1) ? creep
Call: (9) _G2133 is 1 div 1 ? creep
Exit: (9) 1 is 1 div 1 ? creep
Call: (9) _G2136 is 1+ -1 ? creep
Exit: (9) 0 is 1+ -1 ? creep
Call: (9) factorial(0, 1) ? creep
Exit: (9) factorial(0, 1) ? creep
Exit: (8) factorial(1, 1) ? creep
true .
要使factorial(0,3)
的阶乘失败,可以在main子句中添加一个类似N>0的保护谓词
factorial(N,F) :-
N > 0,
X is div(F,N),
N1 is N - 1,
factorial(N1,X).
还有一些改进
这不是一个非常有用的谓词,因为它需要绑定两个参数,如果其中一个是变量,则会失败。在Prolog表示法中,它的签名是阶乘(+N,+F)
更有用的谓词应该有一个签名阶乘(+N,?F)
。这可以通过以下定义来实现
factorial(0,1).
factorial(N,F):-
N > 0,
N1 is N -1,
factorial(N1,X),
F is X * N.
然而,这个定义不是尾部递归的,我们被告知这样的定义不能有效地使用堆栈。我们需要随身携带一件行李
获取阶乘(+N,?F)
的尾部递归定义的部分结果
谢谢,这很有道理。不过,我现在遇到了第二个问题,那就是我不知道如何确保
factorial(0,X)
为任何X
不等于1的值返回false。您可以在main子句中添加一个类似N>0
的保护谓词。N>0
应该是该谓词子句中的必要条件。我不认为它是可选的。@ MITCHELFAAS,我完成了这篇文章,一直到“代码>阶乘< /C>谓词”的“规范”定义。
factorial(N,F):-factorial(N,1,F).
factorial(0,F,F).
factorial(N,X,F):-
N>0,
N1 is N - 1,
X1 is X * N,
factorial(N1,X1,F).