Prolog - 参数未实例化

时间:2015-03-18 19:06:29

标签: prolog clpfd

我试图了解Prolog的工作原理。我正在使用SWI-Prolog。 这里有一些代码:

forall(C1,C2) :- \+ (C1, \+ C2).

foo(N) :- N < 10.

bar(N) :- N > 5.

foobar(N) :- forall(foo(N),bar(N)).

如果我执行以下操作,它会产生所需的输出:

?- foobar(5).
false.

但是当我试图看到所有可能的值时,我得到一个错误:

?- foobar(N).
ERROR: </2: Arguments are not sufficiently instantiated

这里发生了什么?

2 个答案:

答案 0 :(得分:3)

基本上你正在编写一个检查全局含义的程序:

forall N, if N < 10 then N > 5

并且您想知道保留的N的哪个域。

现在你正确地在prolog中重写它:

\+ ( N < 10, \+ ( N > 5 ) ).

但是如果你试图将该查询提供给prolog解释器,它将输出错误:

?- \+ ( N < 10, \+ ( N > 5 ) ).
ERROR: </2: Arguments are not sufficiently instantiated

因为<的参数未实例化。使用简单查询(例如N < 3)也会发生同样的事情。当然,如果在查询之前实例化它,问题就会消失:

?- N=5, \+ ( N < 10, \+ ( N > 5 ) ).
false.

(声明不适用于N = 5)

?- N=6, \+ ( N < 10, \+ ( N > 5 ) ).
N = 6.

(但它适用于N = 6)。

您还可以设置一个谓词,通过回溯为N生成多个分配,代替N=6

?- between(1, 12, N), \+ ( N < 10, \+ ( N > 5 ) ).
N = 6 ;
N = 7 ;
N = 8 ;
N = 9 ;
N = 10 ;
N = 11 ;
N = 12.

但对于大型域名来说,效率非常低(它需要回溯域的每个元素)。

如果您想推理有限域(即整数),可以使用CLPFD库作为@lurker建议。这种方法更有效,因为它具有推理间隔,间隔交叉等等的规则。

您必须使用CLP运算符<>,{{1}替换,\+#<#> },#/\。我们来试试吧:

#\

可能有点难以阅读,但在许多其他方面,它告诉你你正在寻找的答案,即N的域:?- use_module(library(clpfd)). ?- #\ ( N #< 10 #/\ #\ ( N #> 5 ) ). N+1#=_G11193, N#>=6#<==>_G11205, 10#>=_G11193#<==>_G11217, _G11217 in 0..1, _G11217#/\_G11244#<==>0, _G11244 in 0..1, #\_G11205#<==>_G11244, _G11205 in 0..1.

答案 1 :(得分:2)

TL; DR。 Prolog具有很好的代数属性,只要你坚持其逻辑纯的核心。

有关详细信息,请阅读以下关于StackOverflow上Prolog问题的优秀答案: