5

对于我正在编写的程序,我需要制作一个列表列表,其中成对的数字代表一个产品和两个给定数字的总和。

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

这是我所拥有的:

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).

但是,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]]

4

1 回答 1

6

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

这意味着X = 1.它将统一1X您所期望的,但是如果您X = 2.在查询 () 中添加之后X = 1, X = 2.,Prolog 会说false. 背后的原因是你无法统一12而那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),

没有任何信息丢失,因为您不使用AB再次。

现在,让我们暂时忘记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谓词,因为我对变量的漫无边际应该已经告诉您很多关于它的错误之处。

于 2012-04-07T23:55:33.843 回答