我在Prolog中重写了以下函数:
V1:
f(X,Y):- X < 2, Y is X+1.
f(X,3):- 2 =< X, X < 5.
f(X,Y):- 5 =< X, Y is 8-X.
作为V2:
f(X,Y) :-
X < 2,
Y is X + 1.
f(X,Y) :-
X >= 2,
X < 5,
Y is 3.
f(X,Y) :-
X >= 5,
Y is 8-X.
然后我想尝试削减。绿色削减(V3):
f(X,Y) :-
X < 2, !,
Y is X + 1.
f(X,Y) :-
X >= 2,
X < 5, !,
Y is 3.
f(X,Y) :-
X >= 5,
Y is 8-X.
红色切割(V4):
f(X,Y) :-
X < 2, !,
Y is X + 1.
f(X,Y) :-
X < 5, !,
Y is 3.
f(X,Y) :-
Y is 8-X.
但是,我不明白他们的优势,因为删除剪辑会允许代码的相同行为......有什么帮助吗?
答案 0 :(得分:1)
你的所有版本V1..V4在观察上是等价的,所以你有一些推理权。但仍然存在差异。
在许多实现中,V1和V2的效率可能特别低,因为在内部,它们“保持开放的选择点”。这是因为这样的Prologs对其他规则没有任何进一步的看法。因此每个目标f(1,X)
消耗一些内存,只有在回溯(或使用!)时才能释放。这是一个简单的方法来尝试这个:
loop(Goal) :-
Goal,
loop(Goal).
以下是我在SWI中获得的内容:
?- time(loop(f1(1,2))).
% 5,991,554 inferences, 81.282 CPU in 81.443 seconds (100% CPU, 73713 Lips)
ERROR: Out of local stack
?- time(loop(f2(1,2))).
% 5,991,553 inferences, 85.032 CPU in 85.212 seconds (100% CPU, 70462 Lips)
ERROR: Out of local stack
而V3和V4似乎无限期地运行 - 至少比85秒长。像这样的实验对于非常小的程序来说很有趣,但对于较大的程序则不太实用。幸运的是,有一种简单的方法可以告诉许多Prologs查询是否确定执行。要查看您的系统是否执行此操作,请输入:
?- X = 1.
X = 1.
对于您的变体:
?- f1(1,2).
true ; % <== Prolog asked for another answer
false. % <== only to conclude that there is none.
?- f2(1,2).
true ; % same again
false.
?- f3(1,2).
true. % <== Prolog knows there will be no further answer
?- f4(1,2).
true.
虽然V3避免了多余的选择点,但V4现在甚至避免了多余的计算。所以它应该是最有效的。但这是以修复条款顺序为代价的。
然而,V3才有可能,因为绿色削减的两个必要条件是一致的:
非重叠条件。这对你来说应该是显而易见的。
安全测试实例化。这远非显而易见。请注意,目标X < 2
对附加的正确实例化进行了隐式测试!如果X
是未实例化的变量,它会产生实例化错误。正是因为这个测试,V3中的切割恰好是绿色切割。如果没有这种测试,那将是红色的。
另请注意,如果第二条规则是单独的,V1和V2将不相同!因为目标f(X,5).
在V1中会失败,但它会在V2中产生错误。
答案 1 :(得分:0)
正如您所说,第一个版本显示绿色切割和第二个红色切割。 您没有必要感受到这两个版本之间的区别。
a)一个原因可能是效率,但对于快速机器的玩具代码,你几乎没有注意到它。
b)改变规则不应该在绿色削减的情况下改变代码的行为,并且对于第一个代码来说这是正确的。但是在第二个代码中,如果将第二个子句放在第一个子句之前而不是行为更改:f(0,3)为true,但最初它是false。因此,如果你改变规则,你会感到不同。改组的好处在于你不关心订单而是内容 - 这是声明性编程的要点之一。