?- X in 1..100.
X in 1..100.
?- X #> 0, X #< 101.
X in 1..100.
到目前为止一切顺利!
现在让我们假设X
的支持取决于Y in 100..200
。
?- Y in 100..200, X #> 0, X #=< Y.
Y in 100..200,
Y#>=X,
X in 1..200.
X
的域名正确传播到1..200
,约束在X
和Y
之间。
但是...
?- Y in 100..200, X in 1..Y. ERROR: Arguments are not sufficiently instantiated
我认为in
等同于不等式声明,但显然不是。
为什么in
要求两个边界都被接地,是否有任何隐藏的原因?
答案 0 :(得分:3)
这是一个非常好的问题。它有一个直截了当的答案,一开始看起来很深,但是一旦你熟悉了开始变得更广泛可用的声明性调试方法,它就很容易理解。
声明性调试依赖于我们知道总是持有的逻辑程序的属性。纯逻辑程序的一个关键属性(见logical-purity)是单调性:
推广目标可以在增加解决方案集。
以下是一个例子:
?- append([a], [b], [c]). false.
为什么会失败?我们希望找到失败的实际原因,解释为什么不成功。很容易用程序性的术语来思考并且追踪执行,但这很快变得非常复杂,并没有真正向我们展示本质的原因。
相反,我们可以通过系统地概括目标来实现本质。例如,以下成功:
?- append([a], [b], Cs).
我通过将[c]
替换为包含Cs
的{{1}}来概括查询。
您可以自动执行此操作,请参阅Ulrich Neumerkel的library(diadem)
,特别是GUPU系统,这些想法得到充分发展,并帮助您找到Prolog程序中错误的准确位置。
为了最大程度地应用这些技术,你必须在你的程序中保留尽可能多的有趣属性,主要是保留在Prolog的纯粹单调核心中。我说“基本上”是因为这些技术在一类很难正式分类的程序中也有点超出了这个子集。另请注意,许多正在进行的逻辑编程工作旨在尽可能地增加 Prolog的纯单调子集,因此随着时间的推移,限制变得越来越严重。
现在考虑(in)/2
的细节:(in)/2
的域参数在其中包含一个声明性缺陷,因为它使用域的默认表示。有几种方法可以看到这一点,特别是出于以下情况:假设我告诉您域名的格式为inf..High
,那么High
是什么?它可以是:
sup
我们无法清楚地区分这些情况的事实告诉您这种表示是默认的。
干净边界的表示如下所示:
inf
,sup
表示无穷大n(N)
表示整数N
。通过这样的表示,我可以告诉您域名如下:inf..n(N)
,并且您知道 N
必须是整数。
另一个例子:
?- X in 1..0. false.
为什么会失败?让我们再次概括一下找出理由的论点:
?- X in Low..0.
现在假设(in)/2
接受此为可接受的查询。那么Low
是什么?同样,它可以 整数,或原子inf
。遗憾的是,没有明智的方法以这种方式约束Low
:将它约束到整数会使CLP(FD)约束最合适,因此很容易回答:
X in Low..0, Low in inf..0
但这当然太多了,因为它排除了 Low = inf
:
?- Low in inf..0, Low = inf. Type error: `integer' expected, found `inf' (an atom)
知道在这种情况下我们无法确定术语Low
的类型,并且缺乏合理的方式来陈述这一点,会引发实例化错误以表明这样的概括是不予受理。
从这些考虑因素来看,总体答案非常简单:
由于域名的默认表示,
(in)/2
无法轻易推广。
使用此类陈述只有两个原因:
当然,最大的缺点是,他们对你的节目采取陈述性推理的方式。
请注意,在内部,SWI-Prolog 的CLP(FD)系统使用域的干净表示。这也可以转移到用户端,以便我们可以写:
?- X in n(Low)..0.
以声明完全有效的答案:
X in n(Low)..0, Low in inf..0.
随着时间的推移,这些更清晰的符号肯定会找到用户的方式。就目前而言,至少根据我的经验,要问一点太多了。
在您引用的具体示例中,域边界已约束为整数,因此我们当然可以区分这些案例并自动将其转换为不等式。但是,这不会消除defaultyness的核心问题,并且交换目标仍会引发实例化错误。