所以,这里有很多话要说。首先,与大多数声明性语言一样,变量不能真正改变值。
这意味着X = 1.
它将统一1
为X
您所期望的,但是如果您X = 2.
在查询 () 中添加之后X = 1, X = 2.
,Prolog 会说false
. 背后的原因是你无法统一1
,2
而那X
已经真正成为1
,因此X
无法统一2
。
不过,这与 Haskell、Ocaml 等不同,您可以部分绑定变量,如X = h(Y).
. 然后,您将能够进一步统一它X = h(a(Z)).
,而您不能在前面提到的语言中(其中变量实际上只是值的别名)。
为什么他告诉我你想知道的一切?嗯,这是你的主要问题。您首先绑定X
到[6, 5]
,然后期望将其进一步绑定到其他一些东西。一旦一个变量被接地(即它本身不包含任何自由变量),你就不能再改变它的值了。
所以在这里你的递归不会做任何事情,但最终证明是X
错误的。然而,这里并没有,因为您最终addToEnd/3
每次都使用相同的参数调用([6]
和[5]
)[6, 5]
。
话虽如此,让我们看看我们如何改进您的代码。
首先,备注:
multiply(2, 3, Y),
A = Y ,
add(2, 3, Z),
B = Z,
addToEnd([A], [B], X),
可以写
multiply(2, 3, Y),
add(2, 3, Z),
addToEnd([Y], [Z], X),
没有任何信息丢失,因为您不使用A
和B
再次。
现在,让我们暂时忘记addToEnd/3
,想想你想要什么。
如果你进入s1(0, Q)
,你真的想Q
保持自由吗?因为这就是你现在所说的。Q
在这种情况下绑定到更有意义[]
。另外,您很快就会看到,这将成为一个很好的递归基本案例。
s1(0, []).
是说的捷径
s1(0, Q) :- Q = [].
因为 Prolog 在子句头(之前的部分:-
)中进行了统一。
然后,我会作弊一点,但只是为了保持清醒。你可以说,当你从s1(4, Q)
到时,s1(5, Q)
你期望 Q 持有一些微积分的多一个值。
在这里,我们可以声明如下:
s1(N, [SomeCalculus|Q]) :-
PreviousN is N - 1,
s1(PreviousN, Q).
现在,我们只需要给一个值SomeCalculus
:
s1(N, [SomeCalculus|Q]) :-
PreviousN is N - 1,
X is 2 * 3,
Y is 2 + 3,
SomeCalculus = [X, Y],
s1(PreviousN, Q).
或者,如果你正确地遵循,我们可以直接写:
s1(N, [[X, Y]|Q]) :-
PreviousN is N - 1,
X is 2 * 3,
Y is 2 + 3,
s1(PreviousN, Q).
所以完整的程序是:
s1(0, []).
s1(N, [[X, Y]|Q]) :-
PreviousN is N - 1,
X is 2 * 3,
Y is 2 + 3,
s1(PreviousN, Q).
现在,如果您对此进行测试,您可能会注意到当您按下;
键时程序会像您的一样循环。那是因为 Prolog 认为第二个子句也可以适用0
。
所以让我们再次编辑程序:
s1(0, []).
s1(N, [[X, Y]|Q]) :-
N > 0,
PreviousN is N - 1,
X is 2 * 3,
Y is 2 + 3,
s1(PreviousN, Q).
现在一切都很好。
我希望这将帮助您更好地了解如何通过递归构建适当的谓词。我没有花太多时间纠正您的addToEnd/3
谓词,因为我对变量的漫无边际应该已经告诉您很多关于它的错误之处。