Prolog - 递归列表构建

时间:2012-04-07 19:45:30

标签: 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).

但是,例如,当我运行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]]

1 个答案:

答案 0 :(得分:5)

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

这意味着X = 1.会将1统一为X,但是如果您在查询中添加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谓词,因为我对变量的讨论应该已经告诉了你很多关于它的错误。