上一节咱们学习了中断是什么。可是在实际编程时如何使用,为何这个机制会有做用呢?第一个例子,让咱们定义一个没有中断的谓词max/3,其中全部的参数都是整数,而且当第三个参数为前两个参数中较大的一个时,谓词为真。好比,查询:编程
?- max(2, 3, 3). true ?- max(3, 2, 3). true ?- max(3, 3, 3). true
以上都是为真的查询,若是查询:性能
?- max(2, 3, 2). false ?- max(2, 3, 5). false
都是为假的查询。固然,咱们但愿这个谓词在第三个参数为变量的状况下去使用,即咱们可以找到前两个参数的最大值:学习
?- max(2, 3, Max). Max = 3 true ?- max(2, 1, Max). Max = 2 true
如今,能够很容易地实现这个谓词。以下是第一个尝试:测试
max(X, Y, Y) :- X =< Y. max(X, Y, X) :- X > Y.
这是一个完美的程序,咱们可能到此为止了。可是,咱们不该该这样,由于它还不够好。优化
问题是什么?这里存在潜在的性能问题。假设这个谓词会被一个更大型的程序使用,max(3, 4, Y)会被调用。这个谓词会正确地得出结果:Y = 4。可是思考一下若是强制的回溯会致使什么?这个谓词将会使用第二个子句从新进行知足。可是这是没有意义的:3和4的最大值就是4,并且只能是4。没有第二个解决方案了。换种说法:在上面谓词中的两个子句是排他的:若是第一个为真,那么第二个就为假,反之亦然。因此进行回溯尝试彻底是浪费时间。调试
cut能够修正这个问题。咱们应当坚持Prolog不要尝试全部的子句,因此下面的代码能够作到:code
max(X, Y, Y) :- X =< Y, !. max(X, Y, X) :- X > Y.
注意这是如何起做用的。若是max(X, Y, Y)被调用而且X =< Y时,Prolog会达到中断。在这种状况下,第二个参数就是最大值,并且就只有这种解决方案了,中断将这个选择进行提交。在另外一方面,若是X =< Y 失败了,那时Prolog会使用第二个子句进行尝试。变量
注意这个中断没有改变程序的含义。新的代码可以获得和旧代码彻底一致的结果,可是更加高效。事实上,除了中断,这个程序和以前的版本彻底一致,这就是中断有意义的使用场景。像这样不改变程序含义的中断,有一个特殊的名称:绿色中断。程序
可是有一些读者可能不喜欢这样的代码。毕竟,第二个子句是否是有所冗余?若是咱们不得不使用这个子句,那么咱们已经知道第一个参数比第二个参数要大了。咱们可否在中断的帮助下,优化咱们的代码呢?让咱们进行一下尝试,这里是第一次(注:错误的尝试)尝试:技术
max(X, Y, Y) :- X =< Y, !. max(X, Y, X).
注意这个版本除了移除了第二个子句的>测试外,其余都是和以前绿色中断的版本一致的。这个版本如何?对于一些查询而言,是正确的。特别是当第三个参数是变量时,得出的结果是正确的,好比:
?- max(100, 101, X). X = 101 true ?- max(3, 2, X). X = 3 true
然而,和绿色中断版本不一样的是:新的max/3不能正确的工做。思考在三个参数都有值时会发生什么,例如,若是查询:
?- max(2, 3, 2).
显然这个查询是为假的。可是在咱们新的版本中,返回结果为真。为何?由于查询因为不能和第一个子句合一,因此直接使用了第二个子句。这个查询将会和第二个子句合一,因此查询为真。因此移除第二个子句的>测试,并非一个明智的作法。
可是这里存在另一种方式。新版本代码的问题简单地说是因为在到达中断前执行了变量合一。假设咱们将变量处理得更加智能(使用三个变量代替两个),而且在中断后显式地进行合一:
max(X, Y, Z) :- X =< Y, !, Y = Z. max(X, Y, X).
经过验证,这个版本的程序可以正常工做,而且(正如咱们的指望)它避免了以前绿色中断版本中第二个子句的显式比较。
可是这个新的版本和以前绿色中断版本之间有一个重要的区别:新版本中的中断是被称为红色中断的典型例子。技术上来讲,此类中断存在潜在的危险。为何?由于若是咱们去掉此类中断,咱们不能达到彻底一致的程序。即,若是咱们移除中断,程序将没法得出两个参数中的最大值。从另一个角度理解,这个中断的存在对于程序的正确性是不可或缺的。(这和绿色中断不一样——绿色中断都是提升性能)由于红色中断是不可或缺中断,它们的存在乎味着包含它们的程序没有彻底的声明性。如今,红色中断在某些状况下是有用的,可是要当心。使用它们可能致使潜在的错误,或者致使代码难以调试。
那么,应该如何作呢?建议以下:尝试而且得到清晰的,没有中断的程序,同时只是经过中断提升其性能。若是可能,尽可能使用绿色中断。红色中断只有在十分必要时才使用,同时在红色中断处显式地给出注释。经过这种方式,将最大化地平衡代码的清晰的声明性和高效的程序性。