【问题标题】:More trouble with prolog list building序言列表构建的更多麻烦
【发布时间】:2012-04-10 17:54:50
【问题描述】:

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

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

对于那些感兴趣的人,我正在解决的问题是求和/乘积问题,描述为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。我就是想不通。

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

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

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

【问题讨论】:

  • S 在基本情况下用作限制器,以阻止总和大于 100

标签: prolog


【解决方案1】:

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

首先我们调用一个工作者谓词,它将携带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 重新开始,并将X 重置为0

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

如果有什么问题,请在 cmets 中要求澄清。

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

编辑:

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

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

变成

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

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

S 也是如此。本条款中不使用它。它只在第一个中使用。如果您也想在此处的其他条款中记录它的使用,您可以将其替换为 __Sum(良好做法)。然后,您使用两个变量来保存完全相同的值。它在这里没有兴趣。只需将Sum 作为第一个参数调用您的下一个genList/4,而不是为此声明一个新变量。与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&lt; 100 并删除 Sum &lt; 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 已经拥有该值。然后,应该测试&lt; 100 而不是&lt; 101。然后,应该测试X &gt;= 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 &gt;= 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):在计算总和产品等之前增加变量的事实意味着你跳过了一些值。之后尝试增加。让我们作为练习:)

【讨论】:

  • 谢谢莫格,太棒了!我的问题仍然困扰着我,但我不能为此感谢你。我看到了你的个人资料,以及你是如何来到这里提出序言问题并留下来帮助其他人的——希望我也会这样做……尽管正如你所知,我绝对不会帮助序言! :D
  • @XavierNuquos:我编辑了答案以添加解释,我会尽快编辑以解释您的尝试中出了什么问题。
  • @XavierNuquos:我编辑添加了代码审计。有点长对不起;p
  • 完全没有问题,太棒了,非常感谢!我觉得我问了很多问题,如果同一个用户一直问问题,网站上的人会生气吗?只是我已经进入问题的下一阶段,我已经搞砸了一段时间,我又被卡住了,但我有点担心这么快就发布另一个问题......: /
猜你喜欢
  • 1970-01-01
  • 2020-12-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-24
  • 1970-01-01
  • 2019-12-07
相关资源
最近更新 更多