我参加了一门课程,学习了一些序言。我无法弄清楚如何/何时使用削减。即使我得到了切割的一般概念,我也似乎无法正确使用它们。 任何人都可以简单地解释一下,或者给出一个他们可以推荐的“削减”的好教程(那不是learnprolognow.org)吗?
答案 0 :(得分:23)
TL; DR:不要。
剪切修剪Prolog的搜索树。也就是说,鉴于纯粹的Prolog程序没有削减和相同的程序削减,唯一的区别是削减计划可能花费更少的时间在无结果的分支,因此更有效;可能会有更少的答案;它也可能会终止,而原始程序则不会。
听起来很无害......甚至有用,不是吗? 嗯,大部分时间情况都比较复杂。
削减通常以某种方式使用,使得没有削减的程序根本没有明显的意义。这种削减称为红色削减。在更好的情况下,它用于实现粗略形式的非单调否定。而在其他一些情况下,这是一半否定,一半程序性意义很难理解。不仅是程序的读者,也是作者的作者。事实上,这种用途通常无意中缺乏坚定性。在任何情况下:这些剪辑不会放置到现有程序中。他们应该从一开始就参与该计划。
对于此类红色剪辑的更有条理的用途,请更好地使用once/1
,(\+)/1
或(;)/2
- if-then-else,而不是( If -> Then ; Else )
。更好的是,尝试通过发布instantiation_error
来防止此类构造违背意外用途。或者使用iwhen/2
产生实例化错误或when/2
(在SWI,YAP,SICStus中提供)延迟目标。
删除无用的选择点(以及冗余答案)的削减称为绿色削减。但要注意:只需按!和一些#00ff00
,就无法将它们放入程序中。大多数情况下,您需要一个干净的只读防护装置,以确保此切割无法转为#ff0000
。还有一种简单的方法可以安全地删除一些剩余的选择点:call_semidet/1
。以下是一些相关案例:
最后,让我指出cut不是一个提交运算符。它有时会有点像它,但需要很多限制才能成为一个。提交运算符不能(ab)用于实现(\+)/1
。提交要求每个子句彼此独立地进行尝试。因此,每个条款都需要一个完整的后卫只有在首先尝试了其他一些条款之后才能依赖它。此外,必须在谓词的每个子句中进行提交。切割可以在任何地方进行。
答案 1 :(得分:14)
剪切提交 Prolog目标被证明完成了选择。
当程序员知道时,必须使用它,必须 。
最突出的用途是否定失败的实现。
fact(a).
fact(b).
/* 1 */ neg(X) :- call(X), !, fail.
/* 2 */ neg(_).
这里我(重新)定义了标准否定运算符,通常是(\+)/ 1
?- neg(fact(c)).
true.
无法证明规则1的 call(fact(c))
,然后替代规则2成功。
?- neg(fact(a)).
false.
因为fact(a)
可以被证明,所以切换会在失败之前丢弃替代方案。
?- neg(fact(X)).
false.
至少存在一个未知的X,以便事实(X)成功。
?- neg(neg(fact(X))).
true.
双重否定会导致变量不受到约束。这在进行元编程时非常有用,可以在不改变其“结构”的情况下获取子句。从操作角度来看,很清楚(?)发生了什么,但程序确实失去了声明性属性。
另一个仅在基本解释器中有用的用法是指示系统执行最后一次调用,为递归调用添加前缀。然后系统可以避免分配通常所需的堆栈空间来跟踪备用点。一个虚拟的例子:
print_list([E|Es]) :- print_element(E), !, print_list(Es).
print_list([]).
关于教程的编辑:我发现William Clocksin的“Clause and Effect”包含了与剪辑相关的详细调查:第4章“选择和承诺”它完全致力于削减利弊。在底线,主要是... ...
答案 2 :(得分:11)
在使用剪辑之前,我要求我的谓词符合以下两个标准:
一旦我的谓词表现那样,我有时会添加一个剪切来消除不必要的非确定性。
例如,用于测试数字是正数,负数还是零的谓词。
sign(N, positive) :-
N > 0.
sign(N, negative) :-
N < 0.
sign(N, zero) :-
N =:= 0.
每个条款完全独立于其他条款。我可以重新排序这些条款或删除一个条款,其余条款仍然给出预期的答案。在这种情况下,我可能会在positive
和negative
子句的末尾加减,只是为了告诉Prolog系统它通过检查其他子句找不到更多的解决方案。
可以使用-> ;
编写一个类似的谓词而不会删除,但有些人不喜欢它的外观:
sign(N, Sign) :-
( N > 0 -> Sign=positive
; N < 0 -> Sign=negative
; Sign=zero
).
答案 3 :(得分:1)
一旦找到once
谓词,就会从我的代码中删除所有内容。在内部,它就像
once(X) :- X, !.
我发现在做这件事之前做出决定如何做某事非常有用。
例如,这是我的标准元解释器。 maybe1/1
子句在其参数中有唯一的仿函数,所以一旦知道,就可以完美地选择正确的maybe1/1
。
找到该唯一功能的工作是给maybe0/2
预处理器,该预处理器将Y
设置为&#34;做什么声明&#34;关于X
。
如果没有once
,这可能不得不削减。例如。在maybe1
中,我们需要以自上而下的方式检查X/Y
和or
的三种/两种不同解释。但要检查一下 - 没有削减。
maybe(X) :-
once(maybe0(X,Y)), maybe1(Y).
maybe0(true, true).
maybe0((X,Y), (X,Y)).
maybe0((X;Y), or(L)) :- o2l((X;Y),L).
maybe0(X, calls(X)) :- calls(X).
maybe0(X/Y, fact(X/Y)) :- clause(X/_, true).
maybe0(X/Y, rule(X/Y)) :- clause(X/_,_).
maybe0(X/Y, abducible(X/Y)).
maybe0(or([H|T]), or([H|T])).
maybe0(or([]), true).
maybe1(true).
maybe1((X,Y)) :- maybe(X),maybe(Y).
maybe1((X;Y)) :- maybe(X);maybe(Y).
maybe1(abducible(X)) :- assume(X,0).
maybe1(fact(X)) :- assume(X,1), one(X).
maybe1(rule(X)) :- assume(X,2), one(clause(X,Y)), maybe(Y).
maybe1(calls(X)) :- one(clause(X,Y)), maybe(Y).
maybe1(or([H|T])) :- any(One,Rest,[H|T]), ignore(maybe(One)), maybe(or(Rest)).
答案 4 :(得分:1)
Cut谓词可防止回溯。Cut谓词被指定为感叹号(!)。 Cut通过prolog解释器修剪搜索树并缩短路径轨迹。从理论上讲,它总是成功的。
read(a).
read(b).
color(p, red) :- red(p).
color(p,black) :- black(p),!.
color(p,unknown).
?-color(X,Y).
X = a,
Y = red;
X = b,
Y = black;
在不削减目标的情况下,Y =黑色之后,Y =未知。
剪切谓词有两种:
绿色剪切:绿色剪切是一种不影响声明性含义的剪切类型。它仅用于提高效率以及避免不必要的计算。从程序中删除绿色切口不会改变程序的含义。
Red Cut: Red Cut是影响声明性含义的一种。从程序中删除红框会更改程序的含义。