Prolog - 参数未实例化

aka*_*kin 6 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)).
Run Code Online (Sandbox Code Playgroud)

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

?- foobar(5).
false.
Run Code Online (Sandbox Code Playgroud)

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

?- foobar(N).
ERROR: </2: Arguments are not sufficiently instantiated
Run Code Online (Sandbox Code Playgroud)

这里发生了什么?

ffe*_*rri 3

基本上,您正在编写一个程序来检查全局含义:

forall N, if N < 10 then N > 5
Run Code Online (Sandbox Code Playgroud)

你想知道N它适用于哪个领域。

现在您可以在序言中正确地将其重写为:

\+ ( N < 10, \+ ( N > 5 ) ).
Run Code Online (Sandbox Code Playgroud)

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

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

因为 的参数<没有被实例化。对于诸如 之类的简单查询,也会发生同样的情况N < 3。当然,如果您在查询之前实例化它,问题就会消失:

?- N=5, \+ ( N < 10, \+ ( N > 5 ) ).
false.
Run Code Online (Sandbox Code Playgroud)

(该陈述不适用于 N=5)

?- N=6, \+ ( N < 10, \+ ( N > 5 ) ).
N = 6.
Run Code Online (Sandbox Code Playgroud)

(但它适用于 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.
Run Code Online (Sandbox Code Playgroud)

但对于一个大的域来说,这将是非常低效的(它需要对域的每个元素进行回溯)。

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

您必须将<, >, ,,替换\+为 CLP 运算符#<, #>, #/\, #\。我们来尝试一下:

?- 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.
Run Code Online (Sandbox Code Playgroud)

这可能有点难以阅读,但除此之外,它告诉您正在寻找的答案,即 N: 的域N #>= 6