2

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

对于我的程序,我需要制作一个列表列表,每个子列表包含 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).

目标是找到 sum<= 100 的每一对数字。因此,对于一个起始值,X 将找到每一对 1 < X < Y,其中 sum<=100,并用所有数字 2 遍历它-N 将给出可能对的完整列表。

对于那些感兴趣的人,我正在解决的问题是总和/乘积问题,在这里描述(第二页)

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

此外,无法使用内置的 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。我就是想不通。

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

例如 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.

如您所见,每次都会删除一个子列表,我就是不明白为什么!

4

1 回答 1

5

嗯,你快到了。由于您已经在这个问题上花费了相当长的时间,我将向您展示一些有效的代码并对其进行评论:

首先,我们调用一个工人谓词,它将携带XY作为参数并将它们初始化为0

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

然后我们处理我们的基本情况。由于我们从 开始0,基本情况是上限。我们本可以反过来,这只是一个选择。请注意,此处的剪切意味着我们不必担心在我们的以下子句Y中优于,100因为在这种情况下它们不会被执行。

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

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

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并重置X0.

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

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

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

编辑:

现在让我们审核您的代码:D 我将首先评论样式: 在此子句中(称为 fact,因为它没有正文),您只使用一次NX即您不关心存储它们的值。在这种情况下,我们在他们的名字前面加上 a_或者只使用匿名变量_

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

变成

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

或者

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

同样的事情在这里S。本条款中不使用它。它只在第一个中使用。您可以将其替换为_或者_Sum如果您也想在此处的其他条款中记录其使用(良好实践)。然后,您使用两个变量来保存完全相同的值。它在这里没有兴趣。只需将 next用作第一个参数genList/4Sum而不是为此声明一个新变量。与Y和相同N1。该子句的正确版本变为:

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只是 term +(N, X)。这不是价值N + X。您必须is/2像在其他子句中一样使用谓词。与 for 的第二个子句相同的问题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).

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

现在让我们看看它有什么问题,不是在风格上,而是在逻辑上。首先,基本情况。这里一切都很好。你检查总和是你的上限,如果它是 return []。完美的!

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

现在,您的“内部递归”。几乎问题。让我们看看让我感到困扰的细节:您有一个值保留了之前的总和,但计算了一个新值并根据上限 + 1 重新测试它。更好的想法是测试PreviousSum< 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,[[X, Y, Sum, Product]|Xs]):-
    PreviousSum < 100,
    Y is N+1,
    Sum is X + Y,
    Product is X * Y,
    genList(Sum, Y, X, Xs).

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

现在,大灰狼:genList(_PreviousSum, N, X, Q) :- Sum 是 N + X,Sum < 101,NextX 是 X + 1,genList(0, NextX, NextX, Q)。这里有很多话要说。首先,再一次,您不需要计算Sum,因为PreviousSum已经持有该值。然后,应该测试它< 100而不是< 101. 然后,应该对其进行测试X >= N,因为只有在这种情况下,您才不想通过第二个子句,而是在这个子句中。最后但并非最不重要的一点是,genList(0, NextX, NextX, Q)您应该使用 genList(NextX, 0, NextX, Q) 开始它,而不是开始一个新的迭代。在这里,您没有正确重置值。结果子句是:

genList(PreviousSum, N, X, Q) :-
    PreviousSum < 100,
    N >= X,
    NextX is X + 1,
    genList(NextX, 0, NextX, Q).

正如你所看到的,我们知道我们不能通过我们的第二个子句 if 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).

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

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

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

于 2012-04-10T18:40:39.513 回答