所以,这里有很多话要说。首先,与大多数声明性语言一样,变量不能真正改变值。
这意味着什么X = 1.
将统一1
to X
正如你所期望的,但是如果你添加X = 2.
之后在您的查询中(X = 1, X = 2.
), Prolog 会说false
。其背后的原因是你无法统一1
with 2
然后X
真正成为了1
, 所以X
不能统一为2
.
不过,这与 Haskell、Ocaml 等不同,您可以部分绑定变量,如下所示X = h(Y).
。然后您将能够进一步统一它X = h(a(Z)).
,而在前面提到的语言中则不能(其中变量实际上只是值的别名)。
他为什么要告诉我你想知道的一切?嗯,这是你的主要问题。你先绑定X
to [6, 5]
,然后期望将其进一步绑定到其他一些东西。一旦变量被磨平(即其内部不包含任何自由变量),您就无法再次更改其值。
所以在这里你的递归不会做任何事情,但最终会证明X
错误的。然而这里却没有,因为你最终打电话了addToEnd/3
每次都使用相同的参数([6]
, [5]
and [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
and B
again.
现在,让我们忘记addToEnd/3
花点时间想想你想要什么。
如果您输入s1(0, Q)
,你真的想要吗Q
保持自由?因为这就是你现在所说的。绑定更有意义Q
to []
在这种情况下。另外,正如您很快就会看到的那样,这将成为一个很好的递归基本案例。
s1(0, []).
是一个捷径
s1(0, Q) :- Q = [].
因为 Prolog 在子句头中进行了统一(之前的部分:-
).
然后,我会稍微作弊,但只是为了保持清晰。你可以说当从s1(4, Q)
to 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
too.
让我们再次编辑程序:
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
谓词,因为我对变量的漫无目的应该已经告诉你很多关于它的问题所在。