【问题标题】:Custom subset not backtracking自定义子集不回溯
【发布时间】:2016-11-01 13:20:48
【问题描述】:

我是 prolog 的新手,我在做一些作业时遇到了麻烦。 在我的代码的某些部分,我必须在回溯时生成给定集合的子集。意思是,代码应该尝试一个子集,当它失败下一个条件时,尝试下一个子集。我做了一些研究,默认函数subset 不会回溯,因为正如this 问题中所述,两个参数都是input arguments。所以我建立了一个自定义的,它仍然没有回溯。你能告诉我我失败的地方吗?这是我的代码:

营养素(8)。 产品(牛奶,[2,4,6])。 产品(猪排,[1,8])。 产品(酸奶,[3,1])。 产品(亲爱的,[5,7])。 产品(塑料,[3,5,2])。 产品(魔术,[5,7,8])。 nutrientlist(N,L):-findall(I,between(1,N,I),L). subset2([],[]):-!. subset2([X|T],[X|T2]):- subset2(T,T2). subset2([_|T],[T2]):- subset2(T,T2). shopping(K,L):- numNutrients(J), nutrientlist(J,N), findall(P,product(P,_),Z), subset2(X,Z), length(X,T), T =< K, covers(X,N), L = X. covers(_,[]):-!. covers([X|L],N):- product(X,M), subset2(M,N), subtract(N,M,T), covers(L,T). main:- shopping(5,L), write(L).

问题在于谓词购物(K,L)。当它到达谓词subset2时,它给出了长度为6(不是5)的整个集合,然后失败并且不回溯。由于所有先前的谓词都无法回溯,因此它会失败。

那么,为什么subset2 不回溯?

感谢您的宝贵时间。

【问题讨论】:

    标签: prolog subset


    【解决方案1】:

    主要关注点:subset2/2

    首先,让我们关注显示与您预期不同的属性的谓词。

    在你的情况下,这是 only subset2/2,由你定义为:

    子集2([],[]):-!。 子集2([X|T],[X|T2]):- 子集2(T,T2)。 子集2([_|T],[T2]):- 子集2(T,T2)。

    我现在将使用声明式调试来定位问题的原因。

    为了应用此方法,我删除了!/0,因为声明式调试最适合单调逻辑程序。请参阅 了解更多信息。因此,我们将致力于:

    子集2([],[])。 子集2([X|T],[X|T2]):- 子集2(T,T2)。 子集2([_|T],[T2]):- 子集2(T,T2)。

    测试用例

    让我们首先构建一个产生意外答案的测试用例。例如:

    ?-子集2([a],[a,b])。 错误。

    这显然不是的意图。我们可以概括测试用例吗?是的:

    ?-子集2([a],[a,b|_])。 错误。

    所以,我们现在有无数的例子会产生错误的结果。

    练习:是否还有程序过于笼统的情况,即测试用例成功但应该失败? p>

    定位错误

    为什么我们在上述案例中看到了意外失败?为了找出这些错误,让我们概括程序。

    例如:

    子集2(_,[])。 子集2([_|T],[_|T2]):- 子集2(T,T2)。 子集2(_,[T2]):- 子集2(T,T2)。

    即使有了这种大量概括,我们仍然有:

    ?-子集2([a],[a,b|_])。 错误。

    也就是说,我们在很多情况下期望查询成功,但它失败。这意味着剩下的程序,即使它是原始程序的大量概括,仍然过于具体

    修正程序

    为了使展示的案例成功,我们必须要么

    • 添加子句来描述我们需要的案例
    • 更改现有条款以涵盖这些情况

    例如,一种方法是向数据库添加以下子句:

    子集2([a],[a,b|_])。

    我们甚至可以将其概括为:

    子集2([a],[a|_])。

    将这些子句中的一个或两个添加到程序中将使查询成功:

    ?-子集2([a],[a,b|_])。 是的。

    但是,这当然不是我们正在寻找的subset2/2 的一般定义,因为例如在以下情况下它仍然会失败:

    ?-子集2([x],[x,y|_])。 错误。

    因此,让我们选择另一个选项,并更正现有定义。特别是,让我们考虑广义程序的最后一个子句:

    子集2(_,[T2]):- 子集2(T,T2)。

    请注意,这在第二个参数是具有恰好一个元素且受进一步约束的列表时才成立。这似乎太具体了

    因此,我建议您从更改此子句开始,以便它至少使到目前为止收集的测试用例都成功。然后,添加必要的专业化,使其精确成功地用于预期的案例。

    【讨论】:

    • 垫子,我认为子集按原样(错误地)写成它应该是:subset2(Main_Set,Subset)。我的意思是第二个参数应该是子集...... (所以子集2([a],[a,b|_])。应该失败......但不是子集2([a,b|_]),[a])。
    • 在这种情况下,只需交换测试用例的参数即可:subset2([a,b|_], [a]) 应该成功,但目前失败。然后,应用我上面概述的分析。此外,为了使您的意图更清晰,请考虑使用类似 set_subset/2 的名称。
    • 大家好,我知道这是旧的,但我忘了告诉你,编码器是对的。我只需要翻转参数并纠正一元列表问题。我的错。谢谢!
    猜你喜欢
    • 2014-04-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-28
    • 2019-11-18
    • 2014-06-02
    相关资源
    最近更新 更多