Prolog - 递归列表构建

Xav*_*uos 4 prolog

对于我正在编写的程序,我需要列出一个列表,一对数字代表一个产品,两个给定数字的总和.

现在我有一个函数,我可以指定我想在列表中添加一个列表的次数,稍后将使用完整功能进行扩展.

这就是我所拥有的:

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]]

m09*_*m09 5

所以,这里有很多话要说.首先,与大多数声明性语言一样,变量无法真正改变价值.

这也就意味着,X = 1.将统一1X如你所期望的,但如果你添加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)

没有任何信息丢失,因为你不再使用AB.

现在,让我们忘记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谓词,因为我对变量的漫无所谓应该已经告诉了你很多关于它的错误.