我正在写一个阶乘函数的实现。但我不太清楚为什么我的方法失败了:
factorial(0,1).
factorial(N,F) :-
X is div(F,N),
factorial(N-1,X).
我给它测试用例:
?- factorial(1,1).
true
但相反,它给了我一个除0错误。为什么这个函数在达到此值时不会回到factorial(0,1)
的定义,我该如何确保它呢?
答案 0 :(得分:3)
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 is 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)
的阶乘失败,您可以在主要子句中添加N > 0
之类的保护谓词。
factorial(N,F) :-
N > 0,
X is div(F,N),
N1 is N - 1,
factorial(N1,X).
这是一个不太有用的谓词,因为它需要绑定两个参数,如果任何一个变量都会失败。在Prolog表示法中,其签名为factorial(+N,+F)
。
更有用的谓词会有一个签名factorial(+N,?F)
。这可以通过以下定义来完成
factorial(0,1).
factorial(N,F):-
N > 0,
N1 is N -1,
factorial(N1,X),
F is X * N.
然而,这个定义不是尾递归的,我们被告知这样的定义不能有效地使用堆栈。我们需要携带一个
获得factorial(+N,?F)
的尾递归定义的部分结果。
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).