Prolog - 参数未实例化
Prolog - arguments are not instantiated
我正在尝试了解 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
这是怎么回事?
基本上你正在编写一个程序来检查全局含义:
forall N, if N < 10 then N > 5
并且您想知道 N
的哪个域。
现在您在序言中正确地将其重写为:
\+ ( 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.
但是对于一个大域来说,这将是非常低效的(它需要对域的每个元素进行回溯)。
如果您想对有限域(即整数)进行推理,可以按照@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.
这可能有点难读,但在许多其他事情中,它会告诉您正在寻找的答案,即 N 的域:N #>= 6
.
TL;DR. Prolog 具有良好的代数特性,只要您坚持其逻辑纯核心。
有关详细信息,请阅读 Whosebug 上对 Prolog 问题的以下出色回答:
- Features of good Prolog code?
- What is the logical 'not' in Prolog?
- Are cuts that bad in programming?
- Knowing when to use cut in prolog
- Prolog without if and else statements
- Using
\==
or dif
- Why do we use '!' in prolog
- How to prevent Prolog from backtracking where it shouldn't
我正在尝试了解 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
这是怎么回事?
基本上你正在编写一个程序来检查全局含义:
forall N, if N < 10 then N > 5
并且您想知道 N
的哪个域。
现在您在序言中正确地将其重写为:
\+ ( 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.
但是对于一个大域来说,这将是非常低效的(它需要对域的每个元素进行回溯)。
如果您想对有限域(即整数)进行推理,可以按照@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.
这可能有点难读,但在许多其他事情中,它会告诉您正在寻找的答案,即 N 的域:N #>= 6
.
TL;DR. Prolog 具有良好的代数特性,只要您坚持其逻辑纯核心。
有关详细信息,请阅读 Whosebug 上对 Prolog 问题的以下出色回答:
- Features of good Prolog code?
- What is the logical 'not' in Prolog?
- Are cuts that bad in programming?
- Knowing when to use cut in prolog
- Prolog without if and else statements
- Using
\==
ordif
- Why do we use '!' in prolog
- How to prevent Prolog from backtracking where it shouldn't