我是初学者,我一直在做一些功课。 在我的代码的某些部分,我必须在回溯上生成给定集合的子集。意思是,代码应该尝试子集,当它在下一个条件失败时,尝试下一个子集。我做了一些研究,默认函数subset不会回溯,因为正如this中所解释的那样,两个参数都是input arguments。所以我建立了一个自定义的,仍然没有回溯。你能告诉我一些我失败的事吗?这是我的代码:
numNutrients(8).
product(milk,[2,4,6]).
product(porkChops,[1,8]).
product(yoghurt,[3,1]).
product(honey,[5,7]).
product(plastic,[3,5,2]).
product(magic,[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进行回溯?
感谢您的时间。
答案 0 :(得分:1)
subset2/2
首先,让我们将仅集中在谓词上,该谓词显示与您期望的属性不同的属性。
在您的情况下,这只是 subset2/2
,由您定义为:
subset2([], []) :- !. subset2([X|T], [X|T2]) :- subset2(T, T2). subset2([_|T], [T2]) :- subset2(T, T2).
我现在将使用声明性调试来查找问题的原因。
要应用此方法,我删除!/0
,因为声明性调试最适用于纯和单调逻辑程序。有关详细信息,请参阅logical-purity。因此,我们将继续努力:
subset2([], []). subset2([X|T], [X|T2]) :- subset2(T, T2). subset2([_|T], [T2]) :- subset2(T, T2).
让我们首先构建一个产生非预期答案的测试用例。例如:
?- subset2([a], [a,b]). false.
显然不意图。我们可以概括测试用例吗?是:
?- subset2([a], [a,b|_]). false.
所以,我们现在有一个无限的例子系列产生错误的结果。
练习:是否还存在程序过于笼统的情况,即成功的测试用例,尽管它们会失败?
为什么我们在上述案例中看到了意外失败?要找出这些错误,让我们概括该程序。
例如:
subset2(_, []). subset2([_|T], [_|T2]) :- subset2(T, T2). subset2(_, [T2]) :- subset2(T, T2).
即使有大量概括,我们仍然:
?- subset2([a], [a,b|_]). false.
也就是说,我们在很多情况下期望查询成功,但失败。这意味着剩下的程序,即使它是原始程序的大量泛化,仍然太具体。
要使显示的案例成功,我们必须 :
例如,一个出路就是将以下子句添加到数据库中:
subset2([a], [a,b|_]).
我们甚至可以将概括为:
subset2([a], [a|_]).
将这些子句中的任何一个或两个添加到程序中将使查询成功:
?- subset2([a], [a,b|_]). true.
然而,这当然是不我们正在寻找的subset2/2
的一般定义,因为例如在以下情况下仍会失败:
?- subset2([x], [x,y|_]). false.
因此,让我们使用其他选项,并更正现有定义。特别是,让我们考虑一般化程序的最后一个条款:
subset2(_, [T2]) :- subset2(T, T2).
请注意,如果第二个参数是恰好一个元素的列表,则该仅成立,该列表受进一步约束的约束。这似乎太具体了!
因此,我建议您首先更改此子句,以便至少使得到目前为止收集的测试用例成功。然后,添加必要的特化,使其成功成功用于预期的案例。