Prolog代码计算阶乘

3

我是Prolog的新手,正在尝试编写一个计算给定数字的阶乘的代码。 这段代码可以正常工作:

fact(0,1).
fact(N, R) :- N > 0, N1 is N - 1, fact(N1, R1), R is R1 * N.

但这个没有:
fact(0, 1).
fact(N, R) :- N > 0, fact(N - 1, R1), R is R1 * N.

有人可以解释一下吗?

可以重写 fact\2 的第二个版本以每次评估其第一个参数并解决第二个版本中的问题,但按照第一个版本所做的方式(在传递之前评估)更为常规。Prolog 管理参数的“惰性评估”的灵活性可能很有用,但阶乘计算太直接了,无法利用它。 - hardmath
一个挑战是重写任一版本以成为尾递归(符合最后调用优化的条件)。通常需要引入额外的参数(例如 fact/3)来使其工作。 - hardmath
2个回答

4
问题在于Prolog主要使用统一来进行计算。要让它执行算术运算,您需要明确告诉它使用is运算符进行计算。
因此,在您的第一个程序中,您明确告诉它使用子句N1 is N - 1执行减法,因此可以按预期工作。
但是在您的第二个程序中,当您编写fact(N - 1, R1)时,您并没有请求进行算术计算,而是请求进行统一。
如果我定义了fact(5 - 1, foo),那么我可以查询?- fact(N - 1, Y), write([N, Y]),Prolog会愉快地将N5统一,并将Yfoo统一。此查询将输出[5, foo]
因此,更进一步地说,如果我有事实fact(foo - bar),那么查询?- fact(X - Y), write([X, Y])将愉快地统一并返回[foo, bar]-不表示减法-它是所表示的事实结构的一部分。

2

当传递算术表达式(而不是数字)时,需要在特定的时间求值表达式。

(>)/2这样的算术运算符会自动完成这个过程,因此1 > (0+0)的目标成功了,就像1 > 0一样。

隐式单一化(在子句头中)和显式单一化与(=)/2目标表示任意Prolog术语的相等性,而不仅仅是算术表达式。 因此,目标0 = 0成功了,但0 = (1-1)失败了。

对于算术等式(=:=)/20 =:= 00 =:= (1-1)都成功了。

fact/2的第二个定义中,您可以通过编写fact(N,1) :- N =:= 0.而不是fact(0,1).来使第一个子句更通用。额外的好处是,您可以运行类似于?- fact(5+5,F).这样的查询:)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接