让我们从基础开始。cut 运算符是!
并且总是成功的。切口不能回溯过去——它就像一扇单向门。另一种看待它的方式是,它让您致力于您迄今为止所做的决定。
剪辑的用法分为几类。根据定义,绿色剪辑是完全不改变程序运行时语义的剪辑。绿切只是程序员告诉 Prolog 你已经知道你没有解决方案的一种方式。如果 Prolog 不知道没有更多的解决方案,它会询问您是否需要另一个解决方案,然后每次都失败。例如,看看这个实现min/3
:
min1(X, Y, X) :- X < Y.
min1(X, Y, Y) :- Y <= X.
?- min1(3, 4, X).
X = 3 ;
false.
看看 Prolog 是如何问我是否需要另一个解决方案,然后失败了?这证明堆栈上有一个额外的选择点。我们可以通过削减来消除它:
min2(X, Y, X) :- X < Y, !.
min2(X, Y, Y) :- Y =< X.
现在,当我们询问 Prolog 时,min2/3
我们只会得到一个答案:
?- min2(3, 4, X).
X = 3.
看,没有提示,只有一个解决方案。
现在,与红色切割相比,绿色切割的原因在于行为或推理没有真正的变化。只是 Prolog 无法知道这一点X < Y
并且Y =< X
是相互排斥的——绿色切割是我们告诉 Prolog 的方式,一旦你确定 X 小于 Y,检查 Y 是否小于 X 就没有意义了。这会从堆栈中删除选择点,以获得技术性。这有时是一个重要的优化,因为 Prolog 花费大量时间回溯,而花费在尝试不可能的情况上的时间被浪费了。
红色切割是一种有些不同的动物。红色切割是任何确实以超逻辑方式改变行为的切割。为了向您展示红色切割的示例,您可以尝试完全消除第二个测试min/3
:
min3(X, Y, X) :- X < Y, !.
min3(_, Y, Y).
你可能会这样想,“当我们到达第二个子句时,我们知道 X 不小于 Y,所以没有必要检查 Y 是否小于或等于 X。” 乍一看这似乎是合理的,您会发现它似乎具有与 相同的行为min2/3
,知道有一个像以前一样的解决方案:
?- min3(3, 4, X).
X = 3.
?- min3(4, 3, X).
X = 3.
然而,因为我们已经消除了一些逻辑,所以我们向涉及回溯的细微错误敞开了大门。将min/3
其视为三个实体之间的关系,我们可以用来min3/3
制造虚假信息:min1/3
min2/3
?- min1(3, 100, 100).
false.
?- min2(3, 100, 100).
false.
?- min3(3, 100, 100).
true.
100 不是 3 和 100 中的最小值,但min3/3
如果第三个不是变量,则可以肯定具有相同第二个两个值的任何内容。换句话说,我们在使用红色剪辑时min3/3
假设我们永远不会在第三个位置出现值。我们假设我们将使用第三个参数作为“out”参数。
让我们利用这些知识来看看你的家庭作业。我首先想到的是,前两条规则中存在同样的重叠条件逻辑:
calculateCarInsurance(PS,Insurance) :-
PS < 60, ...
calculateCarInsurance(PS,Insurance) :-
PS >= 60, ...
最起码,我们可以在PS < 60
commit之后加上一个绿切:如果PS小于60,那真的不可能也超过60,所以那里的绿切是绝对无害的。
我会提醒人们永远不想添加红色削减。当您需要使用 Prolog 的推理使其按您想要的方式工作时,它们有时是必要的。在这种情况下,您可能会注意到calculateCarInsurance
可能会返回多个解决方案。你可能不想要这种行为,你可能想要承诺你得到的第一个行为。在这种情况下,您可以在所有规则主体的末尾添加一个切口calculateCarInsurance
。然后,无论 Prolog 能找到多少解决方案,您都会得到一个解决方案。这会改变程序的逻辑行为,但根据您的要求可能是可取的。这也可能产生同样的负面影响min3/3
,如果我们想验证保险结果,calculateCarInsurance
可能会确认它永远不会产生的计算。
我希望这足以让你走上正轨。