如何在Prolog中实现Peano数求幂?

时间:2018-09-25 14:55:35

标签: prolog successor-arithmetics

我正在尝试使用下面的代码实现幂运算,但像2 ^ 1(ex(s(s(0)), s(0), Z).)这样的简单查询将永远挂起。

nat(0).
nat(s(X)) :- nat(X).
su(0, X, X) :- nat(X).
su(s(X), Y, s(Z)) :- su(X, Y, Z).

mu(0, _, 0).
mu(s(X), Y, Z) :- su(Y, A, Z), mu(X, Y, A).

ex(_, 0, s(0)).
ex(X, s(Y), Z) :- mu(X, A, Z), ex(X, Y, A).

1 个答案:

答案 0 :(得分:1)

据我所知,它效率不高,因为mu/3是用两个变量调用的。确实:

ex(X, s(Y), Z) :- mu(X, A, Z), ex(X, Y, A).

当时AZ都不为人所知(我用黑体字标出)。

现在您的mu/2无法正确处理此问题。如果我们用mu/3查询mu(s(0), A, Z),则会得到:

?- mu(s(0), A, Z).
A = Z, Z = 0 ;
ERROR: Out of global stack

因此它也陷入了无限递归中。

这是因为它会占用mu/3的第二个子句,并且:

mu(s(X), Y, Z) :- su(Y, A, Z), mu(X, Y, A).

因此su/3用三个未知变量调用。这样做的结果是su/3可以一直“一直到最后”提出建议:

?- su(A, B, C).
A = B, B = C, C = 0 ;
A = 0,
B = C, C = s(0) ;
A = 0,
B = C, C = s(s(0)) ;
A = 0,
...

即使递归mu(X, Y, A)拒绝了所有这些建议,su/3也将永远不会停止提出新的解决方案。

因此,当我们为mu/3ex/3设计谓词时,最好记住这一点。

例如,我们可以在这里使用 accumulator 来累加值,并返回最终产品。这样做的好处是,在进行su/3调用时,我们将使用实数值,例如:

mu(A, B, C) :-
    mu(A, B, 0, C).

mu(0, _, 0, S, S).
mu(s(X), Y, I, Z) :-
    su(Y, I, J),
    mu(X, Y, J, Z).

现在,如果我们输入mu/3且仅固定第一个参数,则会看到:

?- mu(s(0), X, Y).
X = Y, Y = 0 ;
X = Y, Y = s(0) ;
X = Y, Y = s(s(0)) ;
X = Y, Y = s(s(s(0))) ;
...
?- mu(s(s(0)), X, Y).
X = Y, Y = 0 ;
X = s(0),
Y = s(s(0)) ;
X = s(s(0)),
Y = s(s(s(s(0)))) ;
X = s(s(s(0))),
Y = s(s(s(s(s(s(0)))))) ;
...
...

因此,这意味着我们现在至少在固定第一个参数的情况下,不会陷入mu/3的循环中。

我们可以使用相同的策略来定义ex/3谓词:

ex(X, Y, Z) :-
    ex(X, Y, s(0), Z).

ex(X, 0, Z, Z).
ex(X, s(Y), I, Z) :-
    mu(X, I, J),
    ex(X, Y, J, Z).

然后,我们设法计算诸如 2 1 2 2 的指数:

?- ex(s(s(0)), s(0), Z).
Z = s(s(0)) ;
false.
?- ex(s(s(0)), s(s(0)), Z).
Z = s(s(s(s(0)))) ;
false.

请注意,以上内容仍有一些缺陷,例如,计算值4的幂仍然会循环:

?- ex(X, Y, s(s(s(s(0))))).
ERROR: Out of global stack 

通过重写谓词,我们也可以避免这种情况。但我将其保留为练习。