prolog列表构建更麻烦

时间:2012-04-10 17:54:51

标签: prolog

我很抱歉在此发布另一个问题,但我似乎只是在这里转圈!

对于我的程序,我需要列出一个列表,每个子列表包含2个数字,X和Y以及这两个数字的总和和乘积。 到目前为止,我有以下内容:

genList(100,N, X,[]).

genList(S,N, X,[[X,Y,Sum,Product]|Xs]):-

    Y is N+1,
    Sum is X+Y,
    NewS is Sum,
    Sum<101,
    Product is X*Y,
    N1 is N+1,
    genList(NewS,N1, X,Xs).

genList(S,N,X,Q):-
    N+X < 101,
    NextX is X + 1,
    genList(0,NextX,NextX,Q).

目标是找到总和<= 100的每对数字。因此,通过上面的一个起始值,X将找到每对1&lt; X&lt; Y,其中和<= 100,并且所有数字都是2-N,它将给出完整的可能对列表。

对于那些感兴趣的人,我正在解决的问题是总和/产品问题,描述为here(页面上的第二个)

如果有人可以提供帮助,我们将不胜感激!

此外,没有内置的prolog谓词可以使用,因此这样做的复杂方式而不是findall。

此谓词生成的小输出提取如下:

[[5,6,11,30],[5,7,12,35],[5,8,13,40],[5,9,14,45],[5,10,15 ,50],[5,11,16,55],[5,12,17,60],[5,13,​​18,65],[5,14,19,70],[5,15,20 ,75],[5,16,21,80],[5,17,22,85],[5,18,23,90],[5,19,24,95],[5,20,25,100 ],[5,21,26,105],[5,22,27,110],......

我认为它非常接近,但仍然有些不太正确。

它循环通过数字对,但需要使用“;”查看所有答案,这不是我想要的。此外,在所有答案都用完后,它返回false。我只是想不出来。

此外,它给出了起始值的完整答案,但每次都删除一个子列表,直到我只剩下最后一组对。

E.g。 genList(0,48,48,Q)。给了我:

[[48,49,97,2352],[48,50,98,2400],[48,51,99,2448],[48,52,100,2496]]
[[48,49,97,2352],[48,50,98,2400],[48,51,99,2448],[48,52,100,2496],[49,50,99,2450],[49,51,100,2499]]
[[48,49,97,2352],[48,50,98,2400],[48,51,99,2448],[49,50,99,2450],[49,51,100,2499]]
[[48,49,97,2352],[48,50,98,2400],[49,50,99,2450],[49,51,100,2499]]
[[48,49,97,2352],[49,50,99,2450],[49,51,100,2499]]
[[49,50,99,2450],[49,51,100,2499]]
false.

正如您所看到的,每次都会删除子列表,我只是看不清楚原因!

1 个答案:

答案 0 :(得分:5)

嗯,你快到了。由于你已经花了很长时间来解决这个问题,我只会向你展示一些有效的代码并对其进行评论:

首先,我们将工作者谓词称为XY作为参数并将其初始化为0

validPair(Result) :-
    validPair(0, 0, Result).

然后我们处理我们的基本情况。由于我们从0开始,基本情况是上限。我们本可以走另一条路,这只是一个选择。请注意,此处的缩减意味着我们不必担心Y在以下条款中优于100,因为在这种情况下它们不会被执行。

validPair(_X, 101, []) :- !.

现在,X符合100之和的正确限制。我们首先检查一切是否正常,然后我们使用!/0谓词再次阻止执行到达我们的最后一个子句,因为那是没有意义的。完成后我们只需要计算有趣的值并将它们添加到列表中。

validPair(X, Y, [[X, Y, Sum, Product]|R]) :-
    Limit is min(100 - Y, Y),
    X =< Limit,
    !,
    Sum is X + Y,
    Product is X * Y,
    NextX is X + 1,
    validPair(NextX, Y, R).

唯一需要处理的案例是当X超过我们修正的限制时,总和低于100。如果发生这种情况,我们会在下一个Y重新开始,并将X重置为0

validPair(_X, Y, R) :-
    NextY is Y + 1,
    validPair(0, NextY, R).

如果有任何问题,请在评论中要求澄清。

注意:这里使用的剪切是红色剪切 - 即谓词的正确性完全取决于子句的顺序。这是不好的做法。尝试用适当的防护装置补充它们(例如X =< 100),这是一个很好的补充:)

编辑:

现在让我们审核您的代码:D 我将从评论风格开始: 在这个子句中(称为事实,因为它没有正文),你只使用NX一次,即你不关心存储它们的值。在这种情况下,我们使用_为其名称添加前缀,或者只使用匿名变量_

genList(100,N, X,[]).

变成

genList(100, _N, _X, []).

genList(100, _, _, []).

S与此相同。它没有在本节中使用。它仅用于第一个。如果您想在此处的其他条款中记录其使用,您可以将其替换为__Sum(良好做法)。然后,使用两个变量来保存完全相同的值。这里没兴趣。只需使用genList/4作为第一个参数调用下一个Sum,而不是仅为此声明一个新变量。与YN1相同。该条款的正确版本变为:

genList(S,N, X,[[X,Y,Sum,Product]|Xs]):-
    Y is N+1,
    Sum is X+Y,
    NewS is Sum,
    Sum<101,
    Product is X*Y,
    N1 is N+1,
    genList(NewS,N1, X,Xs).

genList(_PreviousSum, N, X,[[X, Y, Sum, Product]|Xs]):-
    Y is N+1,
    Sum is X + Y,
    Sum<101,
    Product is X * Y,
    genList(Sum, Y, X, Xs).

您的最后一个条款存在算术问题:N + X只是术语+(N, X)。它不是值N + X。您必须像使用其他子句一样使用is/2谓词。与S的第二个条款相同的问题。那些小编辑转过来了:

genList(S,N,X,Q):-
    N+X < 101,
    NextX is X + 1,
    genList(0,NextX,NextX,Q).

genList(_PreviousSum, N, X, Q) :-
    Sum is N + X,
    Sum < 101,
    NextX is X + 1,
    genList(0, NextX, NextX, Q).

所以,目前您的更正程序如下:

genList(100, _N, _X, []).
genList(_PreviousSum, N, X,[[X, Y, Sum, Product]|Xs]):-
    Y is N+1,
    Sum is X + Y,
    Sum<101,
    Product is X * Y,
    genList(Sum, Y, X, Xs).
genList(_PreviousSum, N, X, Q) :-
    Sum is N + X,
    Sum < 101,
    NextX is X + 1,
    genList(0, NextX, NextX, Q).

由于它只是样式编辑,因此不会改变其行为。

现在让我们看一下它的错误,不是风格,而是逻辑。 一,基本情况。这里一切都很好。检查总和是否为上限,是否为返回[]。完美!

genList(100, _N, _X, []).

现在,你的“内心递归”。它几乎很好。让我们看一下让我烦恼的细节:你有一个值可以保存你之前的总和,但计算一个新值并重新测试上限+ 1.更好的想法是测试PreviousSum对{{1并删除< 100测试。最好的理由是你有一个争论的事实!此外,更可以理解的是,在该限制情况下,它是用来防止执行该条款的。所以,

Sum < 101

会变成

genList(_PreviousSum, N, X,[[X, Y, Sum, Product]|Xs]):-
    Y is N+1,
    Sum is X + Y,
    Sum<101,
    Product is X * Y,
    genList(Sum, Y, X, Xs).

请注意,此修改有点风格,它不会修改程序行为。它仍然使它更具可读性!

现在,大恶狼:     genList(_PreviousSum,N,X,Q): -         总和是N + X,         总和&lt; 101,         NextX是X + 1,         genList(0,NextX,NextX,Q)。 这里有很多话要说。首先,您不需要计算genList(PreviousSum, N, X,[[X, Y, Sum, Product]|Xs]):- PreviousSum < 100, Y is N+1, Sum is X + Y, Product is X * Y, genList(Sum, Y, X, Xs). ,因为Sum已经拥有该值。然后,应该测试PreviousSum而不是< 100。然后,它应该被测试< 101,因为只有在那种情况下,你不想通过你的第二个条款而是在这个条款中。最后但并非最不重要的是,不应该使用X >= N开始新的迭代,而应该使用genList(NextX,0,NextX,Q)启动它。这里没有正确重置值。结果条款是:

genList(0, NextX, NextX, Q)

如您所见,我们知道如果genList(PreviousSum, N, X, Q) :- PreviousSum < 100, N >= X, NextX is X + 1, genList(NextX, 0, NextX, Q). 我们无法通过我们的第二条款。我们应该加上适当的测试,以确保它是正确的:

N >= X

在这里你完成了,你的程序是正确的!

最终版本是:

genList(PreviousSum, N, X,[[X, Y, Sum, Product]|Xs]):-
    PreviousSum < 100,
    N < X,
    Y is N+1,
    Sum is X + Y,
    Product is X * Y,
    genList(Sum, Y, X, Xs).

变量命名仍然很差。更清晰的版本是:

genList(100, _N, _X, []).
genList(PreviousSum, N, X,[[X, Y, Sum, Product]|Xs]):-
    PreviousSum < 100,
    N < X,
    Y is N+1,
    Sum is X + Y,
    Product is X * Y,
    genList(Sum, Y, X, Xs).
genList(PreviousSum, N, X, Q) :-
    PreviousSum < 100,
    N >= X,
    NextX is X + 1,
    genList(NextX, 0, NextX, Q).

在这里你仍然有一个问题(是的,它结束了:D):在计算总和产品等之前增加变量的事实意味着你跳过一些值。尝试增加之后。这是一个练习:)