嗯,你快到了。由于您已经在这个问题上花费了相当长的时间,所以我将向您展示一些有效的代码并对其进行评论:
首先我们调用一个工作者谓词,它将携带X 和Y 作为参数并将它们初始化为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 重新开始,并将X 重置为0。
validPair(_X, Y, R) :-
NextY is Y + 1,
validPair(0, NextY, R).
如果有什么问题,请在 cmets 中要求澄清。
注意:这里使用的切分是红色切分——即谓词的正确性完全取决于子句的顺序。这是不好的做法。尝试用适当的守卫来补充它们(例如X =< 100),这将是一个很好的补充:)
编辑:
现在让我们审核您的代码:D
我将从评论风格开始:
在这个子句中(称为 fact 因为它没有正文),您只使用一次N 和X,即您不关心存储它们的值。在这种情况下,我们在他们的名字前面加上 _ 或者只使用匿名变量 _:
genList(100,N, X,[]).
变成
genList(100, _N, _X, []).
或
genList(100, _, _, []).
S 也是如此。本条款中不使用它。它只在第一个中使用。如果您也想在此处的其他条款中记录它的使用,您可以将其替换为 _ 或 _Sum(良好做法)。然后,您使用两个变量来保存完全相同的值。它在这里没有兴趣。只需将Sum 作为第一个参数调用您的下一个genList/4,而不是为此声明一个新变量。与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 只是术语+(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 和 < 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) :-
总和是 N + X,
总和 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).
变量命名还是很差。更清晰的版本是:
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):在计算总和产品等之前增加变量的事实意味着你跳过了一些值。之后尝试增加。让我们作为练习:)