所以,明确一下,我们正在谈论这个程序:
inside(a,b).
inside(a,c).
inside(b,d).
r_inside(X,Y) :- inside(X,Y).
r_inside(X,Y) :- r_inside(X,Z), inside(Z,Y).
和查询:
?- r_inside(a, X).
X = b ;
X = c ;
X = d ;
不终止
为了突出查询的“不终止”方面(而不被任何具体解决方案所分散注意力),我们可以使用:
?- r_inside(a, X), false.
不终止
要找到不终止的原因,我们可以使用基于程序切片的强大声明式调试方法(请参见failure-slice)。思路是在程序中插入false/0
,以便找到更小的(理想情况下:最小的)代码片段,它们仍然不会终止。
例如,考虑您代码的以下版本:
```
inside(a,b) :- false.
inside(a,c) :- false.
inside(b,d) :- false.
r_inside(X,Y) :- false, inside(X,Y).
r_inside(X,Y) :- r_inside(X,Z), false, inside(Z,Y).
```
使用这个版本,我们仍然得到:
```
?- r_inside(a, X), false.
nontermination
```
现在我使用删除线来划掉先前版本中那些可以忽略的部分,因为它们不能导致非终止:
```
inside(a,b) :- false.
inside(a,c) :- false.
inside(b,d) :- false.
r_inside(X,Y) :- false, inside(X,Y).
r_inside(X,Y) :- r_inside(X,Z),
false, inside(Z,Y).
```
因此,我们将其简化为以下关于非终止的
解释,因为这是唯一剩下的片段:
```
r_inside(X,Y) :- r_inside(X,Z).
```
这个“片段”本身就会导致非终止。无论你添加什么事实,或者在单个剩余目标之后添加什么目标,都不能阻止这种情况发生。
因此,要解决这个问题,你必须改变这个“片段”。
你还可以通过考虑Prolog的实际执行策略来“操作性地”解释非终止。特别是,你可以使用其执行的“深度优先”方面进行论证。然而,为什么要费心去做这样低级的解释呢?你可以自动生成失败切片,它们让你更直接地看到非终止的实际原因。