Learn Prolog Now 翻译 - 第十章 - 中断和否认 - 第一节, 中断

中断



自动回溯是Prolog中颇有表明性的一个特征。可是回溯可能会致使低效。有时Prolog会浪费时间在一些没有结果的可能性搜索上。若是在回溯行为方面有一些控制机制的话,会是一件比较有意义的事情,可是直到如今为止咱们看到只有两种至关初级的方式能够用于这个目的:交换规则顺序,和交换目标顺序。其实有另一种方式:存在一个内置的谓词:!(英文感叹号),称为中断,能够提供一种更为直接地控制Prolog搜寻解决方案的方式。学习

到底中断是什么,它是如何起做用的?它实际上是一个特殊的原子,咱们能够在子句中使用它。看例子:code

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

上面的例子就是一个完美的Prolog规则。中断是这样起做用的:首先,它是一个永远成功的目标;其次,更为重要的是,它有一个反作用。假设有其余一些目标会使用这个子句(咱们称之为父目标,好比例子中的p(X)),在进行搜索时,中断会使得规则左边的目标没法回溯,而只能回溯规则右边的目标。让咱们经过例子来学习。blog

首先思考没有中断的代码:原理

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)。程序

如今假设咱们在第二个子句中加入中断:im

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

若是如今查询p(X),会获得以下的答案:d3

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)不能在进行,可是须要回溯寻找新的选择时,搜索已经被中止:db

有一个须要强调的要点:中断会提交全部的选择,这些选择是为了知足父目标而将包含中断的子句合一时作出的,而且是从中断左端进行合一的那些选择。好比,在以下规则的模式中:

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的第二个规则子句。

以下式对应的搜索树:

相关文章
相关标签/搜索