上一節咱們學習了中斷是什麼。可是在實際編程時如何使用,爲何這個機制會有做用呢?第一個例子,讓咱們定義一個沒有中斷的謂詞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).
經過驗證,這個版本的程序可以正常工做,而且(正如咱們的指望)它避免了以前綠色中斷版本中第二個子句的顯式比較。
可是這個新的版本和以前綠色中斷版本之間有一個重要的區別:新版本中的中斷是被稱爲紅色中斷的典型例子。技術上來講,此類中斷存在潛在的危險。爲何?由於若是咱們去掉此類中斷,咱們不能達到徹底一致的程序。即,若是咱們移除中斷,程序將沒法得出兩個參數中的最大值。從另一個角度理解,這個中斷的存在對於程序的正確性是不可或缺的。(這和綠色中斷不一樣——綠色中斷都是提升性能)由於紅色中斷是不可或缺中斷,它們的存在乎味着包含它們的程序沒有徹底的聲明性。如今,紅色中斷在某些狀況下是有用的,可是要當心。使用它們可能致使潛在的錯誤,或者致使代碼難以調試。
那麼,應該如何作呢?建議以下:嘗試而且得到清晰的,沒有中斷的程序,同時只是經過中斷提升其性能。若是可能,儘可能使用綠色中斷。紅色中斷只有在十分必要時才使用,同時在紅色中斷處顯式地給出註釋。經過這種方式,將最大化地平衡代碼的清晰的聲明性和高效的程序性。