Learn Prolog Now 翻譯 - 第十章 - 中斷和否定 - 第一節, 中斷


中斷



自動回溯是Prolog中很有代表性的一個特征。但是回溯可能會導致低效。有時Prolog會浪費時間在一些沒有結果的可能性搜索上。如果在回溯行為方面有一些控制機制的話,會是一件比較有意義的事情,但是直到現在為止我們看到只有兩種相當初級的方式可以用於這個目的:交換規則順序,和交換目標順序。其實有另外一種方式:存在一個內置的謂詞:!(英文感嘆號),稱為中斷,可以提供一種更為直接地控制Prolog搜尋解決方案的方式。

到底中斷是什么,它是如何起作用的?它其實是一個特殊的原子,我們可以在子句中使用它。看例子:

p(X) :- b(X), c(X), !, d(X), e(X).

上面的例子就是一個完美的Prolog規則。中斷是這樣起作用的:首先,它是一個永遠成功的目標;其次,更為重要的是,它有一個副作用。假設有其他一些目標會使用這個子句(我們稱之為父目標,比如例子中的p(X)),在進行搜索時,中斷會使得規則左邊的目標無法回溯,而只能回溯規則右邊的目標。讓我們通過例子來學習。

首先思考沒有中斷的代碼:

p(X) :- a(X).
p(X) :- b(X), c(X), d(X), e(X).

p(X) :- f(X).

a(1). b(1). b(2). c(1). c(2). d(2). e(2). f(3).

如果查詢p(X),我們會得到如下的答案:

?- p(X).
X = 1;
X = 2;
X = 3;
false

下面是對應的搜索樹,注意其中必須回溯的地方,當進入第二個子句,決定滿足第一個目標b(1),回溯后被替換為b(2)。

現在假設我們在第二個子句中加入中斷:

p(X) :- b(X), c(X), !, d(X), e(X).

如果現在查詢p(X),會得到如下的答案:

X = 1;
false

這里發生了什么?讓我們思考一下。

  1. p(X)首先和第一個子句合一,所以得到新的目標 a(X)。通過將X初始化為1,Prolog將a(X)和事實a(1)合一,從而找到了一個解決方案。目前為止,發生的一切和第一個版本是一致的。
  2. 當我們繼續搜索第二個解決方案。p(X)與第二個規則合一,所以我們獲得新的目標:b(X), c(X), !, d(X), e(X)。通過將X初始化為1,Prolog將b(X)和b(1)合一,所以我們獲得新的目標:c(1), !, d(1), e(1)。同時c(1)是知識庫中存在的事實,所以目標簡化為:!, d(1), e(1)。
  3. 現在到了發生巨變的時刻。目標!為真(正如其定義的,這是一個永真的目標)並且會提交目前為止的選擇。具體來說,我們會提交X = 1,同時我們也會提交使用的第二個規則。
  4. 但是d(1)失敗了。這樣我們就無法滿足目標p(X)。當然,如果我們允許重試將X初始化為2,我們能夠使用第二個規則去生成一個解決方案(就是在原始版本程序中發生的)。但是這里我們無法這樣做:中斷已經在搜索樹種刪除了這種可能性。同時,如果允許嘗試第三個規則,也可以生成X = 3的解決方案。但是我們還是無法這樣做:中斷同樣從搜索樹中刪除了這種可能性。

如果觀察如下的搜索樹,你會發現一些樹枝被刪除:當目標d(1)不能在進行,但是需要回溯尋找新的選擇時,搜索已經被停止:

有一個需要強調的要點:中斷會提交所有的選擇,這些選擇是為了滿足父目標而將包含中斷的子句合一時做出的,並且是從中斷左端進行合一的那些選擇。比如,在如下規則的模式中:

q :- p1, ..., pn, !, r1, ..., rm

當我們到達中斷的時候,Prolog會提交為了滿足q的並且包含中斷的子句的所有選擇,並且這些選擇是運算p1, ..., pm得出的。然而,在r1, ..., rm內,我們能夠進行回溯,並且滿足目標q之前的其他選擇我們也可以進行回溯。通過看下面的例子來明確這些原理。

首先思考沒有中斷的程序:

s(X, Y) :- q(X, Y).
s(0, 0).

q(X, Y) :- i(X), j(Y).

i(1).
i(2).
j(1).
j(2).
j(3).

如下是查詢和結果:

?- s(X, Y).

X = 1
Y = 1;

X = 1
Y = 2;

X = 1
Y = 3;

X = 2
Y = 1;

X = 2
Y = 2;

X = 2
Y = 3;

X = 0
Y = 0;
false

下面是對應的搜索樹:

假設我們在q/2子句中加入中斷:

q(X, Y) :- i(X), !, j(Y).

現在程序的行為如下:

?- s(X, Y).

X = 1
Y = 1;

X = 1
Y = 2;

X = 1
Y = 3;

X = 0
Y = 0;
false

讓我們看看為什么:

  1. s(X, Y)首先和第一個規則合一,這會給出新的目標:q(X, Y)。
  2. q(X, Y)接着和第三個規則合一,這會給出新的目標:i(X), !, j(Y)。通過將X初始化為1,Prolog將i(X)和事實i(1)合一,這會得出新的目標:!, j(Y)。中斷當然為真,同時會提交直到現在為止做出的選擇。
  3. 但是有哪些選擇呢?這里存在:X = 1,和我們正在使用的子句。但是注意:我們沒有為Y選擇任何的值。
  4. Prolog會繼續,通過將Y初始化為1,Prolog將j(Y)和事實j(1)合一,所以我們找到了一個解決方案。
  5. 但是我們能夠找到更多解決方案。Prolog能夠對Y嘗試其他的值。所以回溯並且將Y初始化為2,所以這樣找到了第二種解決方案。事實上還可以繼續找到解決方案:再次回溯,通過將Y初始化為3,找到第三種解決方案。
  6. 但是這些都是搜索j(X)的匹配值,在中斷左邊的回溯是不允許的,所以無法將X重新初始化為2,所以這里無法找到類似沒有中斷程序中X = 2的那些解決方案。回溯到達q(X, Y)之前的目標是允許的,所以Prolog會找到s/2的第二個規則子句。

如下式對應的搜索樹:


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com