对于我正在编写的程序,我需要列出一个列表,一对数字代表一个产品,两个给定数字的总和.
现在我有一个函数,我可以指定我想在列表中添加一个列表的次数,稍后将使用完整功能进行扩展.
这就是我所拥有的:
s1(0, X).
s1(Q, X) :-
N is Q - 1,
multiply(2, 3, Y),
A = Y ,
add(2, 3, Z),
B = Z,
addToEnd([A], [B], X),
s1(N,X).
multiply(A, B, C):-
C is A * B.
add(A, B, C) :-
C is A + B.
addToEnd([], L, L).
addToEnd([H|T], L2, [H|L3]) :-
addToEnd(T, L2, L3).
Run Code Online (Sandbox Code Playgroud)
但是,当我运行时s1(2,X)
,我得到了[6,5]
返回,然后没有别的,它只是挂起.当我跑s1(0,X)
,我得到true
,然后false
当我击中;
谁能帮我这个?我看不出我做错了什么,我觉得它应该有效!
为了澄清我觉得这应该如何工作:我打电话s1(2,X).
N = 1
,[6,5]
添加到列表中X([[6,5]])
s1(1,X).
N=0
,[6,5]
添加到列表中X ([[6,5],[6,5]])
s1(0,X).
X = [[6,5],[6,5]]
所以,这里有很多话要说.首先,与大多数声明性语言一样,变量无法真正改变价值.
这也就意味着,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),
Run Code Online (Sandbox Code Playgroud)
可以写
multiply(2, 3, Y),
add(2, 3, Z),
addToEnd([Y], [Z], X),
Run Code Online (Sandbox Code Playgroud)
没有任何信息丢失,因为你不再使用A
和B
.
现在,让我们忘记addToEnd/3
片刻,想想你想要什么.
如果你进入s1(0, Q)
,你真的想Q
保持自由吗?因为这就是你现在所说的.它会做更多的结合感Q
,以[]
在这种情况下.此外,这将成为一个很好的递归基础案例,你很快就会看到.
s1(0, []).
Run Code Online (Sandbox Code Playgroud)
是一个快捷方式
s1(0, Q) :- Q = [].
Run Code Online (Sandbox Code Playgroud)
因为Prolog在子句头部(之前的部分:-
)中进行了统一.
然后,我会欺骗一点,但这只是为了保持清醒.你可以说,当你去往时s1(4, Q)
,s1(5, Q)
你期望Q再拿一些微积分值.
在这里,我们可以说如下:
s1(N, [SomeCalculus|Q]) :-
PreviousN is N - 1,
s1(PreviousN, Q).
Run Code Online (Sandbox Code Playgroud)
现在,我们只需给出一个值SomeCalculus
:
s1(N, [SomeCalculus|Q]) :-
PreviousN is N - 1,
X is 2 * 3,
Y is 2 + 3,
SomeCalculus = [X, Y],
s1(PreviousN, Q).
Run Code Online (Sandbox Code Playgroud)
或者,如果你正确地遵循,我们可以直接写:
s1(N, [[X, Y]|Q]) :-
PreviousN is N - 1,
X is 2 * 3,
Y is 2 + 3,
s1(PreviousN, Q).
Run Code Online (Sandbox Code Playgroud)
所以完整的程序将是:
s1(0, []).
s1(N, [[X, Y]|Q]) :-
PreviousN is N - 1,
X is 2 * 3,
Y is 2 + 3,
s1(PreviousN, Q).
Run Code Online (Sandbox Code Playgroud)
现在,如果你测试它,你可能会注意到程序在你按下;
键时就像你的一样循环.那是因为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).
Run Code Online (Sandbox Code Playgroud)
现在一切都很好.
我希望这能帮助您更好地理解如何通过递归构建正确的谓词.我没有花太多时间来纠正你的addToEnd/3
谓词,因为我对变量的漫无所谓应该已经告诉了你很多关于它的错误.