我刚开始学prolog,我一直在做一些作业。在我代码的某些部分,我必须在回溯时生成给定集合的子集。这意味着,代码应该尝试一个子集,当它无法满足下一个条件时,再尝试下一个子集。我已经做了一些研究,默认函数子集不会回溯,因为正如在这个问题中解释的那样,两个参数都是输入参数。所以我做了一个定制的,它仍然不能回溯。你能给我提示一下我做错了什么吗?下面是我的代码:<>之前numNutrients(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不回溯呢?
感谢您的宝贵时间。
主要焦点:subset2/2
首先,让我们只关注显示与您期望的属性不同的谓词。
在您的情况下,这是只有 subset2/2
,由您定义为:
要应用此方法,我删除!/0
,因为声明式调试在纯和单调逻辑程序上效果最好。有关更多信息,请参阅逻辑纯度。因此,我们将处理:
让我们首先构造一个测试用例,产生非预期的答案。例如:
<>之前?- subset2([a], [a,b])。假。之前显然不是的本意。我们可以概括测试用例吗?是的:
<>之前- subset2([a], [a,b] |_])。假。之前所以,我们现在有一个无限的例子族,产生错误的结果。
练习:是否也存在程序过于一般化的情况,也就是说,测试用例成功,尽管它们应该失败?
<标题> 定位错误为什么在上述情况下会出现意外失败?为了找出这些错误,让我们概括程序。
例如:<>之前subset2 ( _ ,[])。subset2([_|T], [_|T2]):-subset2 (T, T2)。subset2(_, [T2]):-subset2 (T, T2)。之前即使有了这个大量的概括,我们仍然有:
<>之前- subset2([a], [a,b] |_])。假。之前也就是说,在很多情况下,我们期望查询成功,但是失败。这意味着剩余的程序,即使它是原始程序的大量泛化,仍然是太特定。
纠正程序
要使所示的案例成功,我们必须:
- 添加子句,描述我们需要的情况
- 或更改现有条款,使涵盖这些情况<nbsp;>。
我们甚至可以将归纳为:
<>之前subset2([],[| _))。之前在程序中添加其中一个或两个子句将使查询成功:
<>之前- subset2([a], [a,b] |_])。。之前然而,这当然不是我们正在寻找的subset2/2
的一般定义,因为它仍然会失败,例如:
<>之前?- subset2([x], [x,y|_])。假。之前因此,让我们使用另一个选项,并且更正现有的定义。特别地,让我们考虑一般化程序的最后一个子句:
<>之前subset2(_, [T2]):-subset2 (T, T2)。之前注意,这个只有当第二个参数是一个只有一个元素的列表且受进一步约束时,才成立。这似乎太特殊了!
因此,我建议您从更改此子句开始,以便它至少使到目前为止收集的所有测试用例都成功。然后,添加必要的专门化,以使它能够精确地用于预期的情况。 标题>标题>