为什么在调用外部谓词时递归函数会失败会使它成功?

时间:2018-03-16 16:23:38

标签: recursion prolog

我正在写一个阶乘函数的实现。但我不太清楚为什么我的方法失败了:

factorial(0,1).
factorial(N,F) :-
    X is div(F,N),
    factorial(N-1,X).

我给它测试用例:

?- factorial(1,1).
true

但相反,它给了我一个除0错误。为什么这个函数在达到此值时不会回到factorial(0,1)的定义,我该如何确保它呢?

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).