如何在 Prolog 中实现 Peano 数求幂?
How to implement Peano numbers exponentiation in Prolog?
我正在尝试使用下面的代码实现求幂,但是像 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).
据我所知,它效率不高,因为 mu/3
是用 two 个自由变量调用的。确实:
ex(X, s(Y), <b>Z</b>) :- mu(X, <b>A</b>, <b>Z</b>), ex(X, Y, A).
当时 A
和 Z
未知 (我已将它们标为粗体)。
现在您的 mu/2
无法正确处理此问题。如果我们用 mu(s(0), A, Z)
查询 mu/3
,我们得到:
?- mu(s(0), A, Z).
A = Z, Z = 0 ;
ERROR: Out of global stack
所以它也陷入了无限递归。
这是因为它会采用mu/3
的第二个子句,并且:
mu(s(X), <b>Y</b>, <b>Z</b>) :- su(<b>Y</b>, <b>A</b>, <b>Z</b>), mu(X, Y, A).
所以 su/3
是用三个未知变量调用的。这样做的效果是 su/3
可以继续提议值 "until the end of times":
?- 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/3
和 ex/3
.
设计谓词时,最好记住这一点
例如,我们可以在此处使用 累加器 来累加值,并 returns 最终产品。这样做的好处是,我们在进行 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).
然后我们设法计算指数,如 21 和 22:
?- 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
通过重写谓词,我们也可以避免这种情况。但我把它留作练习。
我正在尝试使用下面的代码实现求幂,但是像 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).
据我所知,它效率不高,因为 mu/3
是用 two 个自由变量调用的。确实:
ex(X, s(Y), <b>Z</b>) :- mu(X, <b>A</b>, <b>Z</b>), ex(X, Y, A).
当时 A
和 Z
未知 (我已将它们标为粗体)。
现在您的 mu/2
无法正确处理此问题。如果我们用 mu(s(0), A, Z)
查询 mu/3
,我们得到:
?- mu(s(0), A, Z).
A = Z, Z = 0 ;
ERROR: Out of global stack
所以它也陷入了无限递归。
这是因为它会采用mu/3
的第二个子句,并且:
mu(s(X), <b>Y</b>, <b>Z</b>) :- su(<b>Y</b>, <b>A</b>, <b>Z</b>), mu(X, Y, A).
所以 su/3
是用三个未知变量调用的。这样做的效果是 su/3
可以继续提议值 "until the end of times":
?- 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/3
和 ex/3
.
例如,我们可以在此处使用 累加器 来累加值,并 returns 最终产品。这样做的好处是,我们在进行 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).
然后我们设法计算指数,如 21 和 22:
?- 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
通过重写谓词,我们也可以避免这种情况。但我把它留作练习。