我想写的是 "如果你以前拿到球,自此不给球,你就有了球 "这样的内容。
:- use_module(library(clpfd)).
time(T1, has_ball) :-
time(T2, get_ball),
T2 #=< T1,
\+ (time(T3, give_ball),
T2 #< T3, T3 #< T1).
time(0, get_ball).
time(2, give_ball).
这可以正确地回答关于特定时间T的直接问题(通过提供T,例如用标签)。
?- time(1, has_ball).
true.
?- T in 0..9, label([T]), time(T, has_ball).
T = 0 ;
T = 1 ;
T = 2 ;
false.
但是当被要求找到所有有效的时间T时,我只是得到了错误的答案。
?- time(T, has_ball).
false.
根据我的理解,clpfd在没有标注的情况下,对结果做了一些过度的近似,所以我本以为类似 "T in inf...sup, time(T, has_ball). "这样的东西会告诉我使用标注。但是很明显我错了,现在我怕在其他情况下会漏掉解决方案。谁能帮我理解一下?
EDIT:伊莎贝尔新兵的回答让我意识到,其实我的意思是。
time(HasBall, has_ball) :-
time(GetBall, get_ball),
GetBall #=< HasBall,
\+ (time(GiveBall, give_ball),
GetBall #< GiveBall #/\ GiveBall #< HasBall).
因为我的意思是 "如果你在之前的GetBall时间得到了球,并且没有给它SINCE,那么在HasBall时间你仍然拥有它"。所以 time(GiveBall, give_ball)
需要在否定式中。这里用 \+
与 #\
给出了一个新的错误("Domain error: `clpfd_reifiable_expression' expected"),我将进行调查。
简而言之,你不应该混合使用Prolog的否定式 \+
与CLP(FD)。CLP(FD)有自己的否定运算符,在其约束条件上起作用,写为 #\
. 所以你可以把你的谓词写成。
time(HasBall, has_ball) :-
time(GetBall, get_ball),
GetBall #=< HasBall,
time(GiveBall, give_ball),
#\ (GetBall #< GiveBall #/\ GiveBall #< HasBall).
我重命名了你的变量,因为我不太明白是怎么回事。现在有点明白了,但是否定的约束条件是不是应该用正向的 HasBall #=< GiveBall
无论如何,这都是我相信你所希望的。
?- time(T, has_ball).
T in 0..2.
?- time(T, has_ball), label([T]).
T = 0 ;
T = 1 ;
T = 2.
?- time(1, has_ball).
true.
?- T in 0..9, label([T]), time(T, has_ball).
T = 0 ;
T = 1 ;
T = 2 ;
false.
为了更清楚地理解发生了什么事 我们可以把你的原始子句用常量值代替变量
step1(T1) :-
T2 = 0,
T2 #=< T1,
\+ ( T3 = 2, T2 #< T3, T3 #< T1 ).
step2(T1) :-
0 #=< T1,
\+ ( 0 #< 2, 2 #< T1 ).
step3(T1) :-
0 #=< T1,
\+ ( 2 #< T1 ).
所以在最后一步之后 你的谓词在调用一个有约束变量和一个无约束变量时的行为基本上是这样的:
?- T1 = 1, 0 #=< T1, \+ (2 #< T1).
T1 = 1.
?- 0 #=< T1, \+ (2 #< T1).
false.
这是因为在第一种情况下,最后的目标是: \+ (2 #< 1)
,它的成功是因为 2 #< 1
失败。
但如果你不绑定 T1
那么 2 #< T1
继承:
?- 2 #< T1.
T1 in 3..sup.
所以它的否定性 \+ (2 #< T1)
失败。这个目标本质上是说 "没有大于2的数",这是错误的。相比之下,CLP(FD)的否定在 "相反 "约束下成功。
?- #\ (2 #< T1).
T1 in inf..2.
在你的程序中,这几乎可以肯定是更有意义的, 因为它尊重数学属性,即不是(A < B)等于(A >= B):
?- 2 #>= T1.
T1 in inf..2.
EDIT: 我忽略了一个事实,也许没有 give_ball
事件已被看到,在这种情况下,人们仍然会持有球。你不能使用 #\
试图用你的方式来建模,因为 #\
适用 只是 CLP(FD)约束(特别是 "reifiable "约束),而不是 "普通 "的Prolog目标。你也不能这样混合这些层次。
所以你需要对存在的两种情况更加明确。如果有两种情况,你还没有放弃球:
这里在Prolog中也是一样的,把应用Prolog否定的地方和应用CLP(FD)否定的地方分开。
has_not_given_up_ball(HasBall) :-
time(GiveBall, give_ball),
\# ( GetBall #< GiveBall #/\ GiveBall #< HasBall ).
has_not_given_up_ball(_HasBall) :-
\+ time(_GiveBall, give_ball).
(再次强调, 我认为你应该用 HasBall #=< GiveBall
而不是被否定的约束)。)
然后你可以这样调整你的定义。
time(HasBall, has_ball) :-
time(GetBall, get_ball),
GetBall #=< HasBall,
has_not_given_up_ball(HasBall).
如果一个 time(2, give_ball)
事实存在,这和以前一样,但多了一个选择点。如果我把这个事实注释出来,它的模型正确的是,球没有被放弃,所以一个人持有球的时间更长。
?- time(T, has_ball).
T in 0..sup.
?- time(T, has_ball), label([T]).
ERROR: Arguments are not sufficiently instantiated
...
?- time(1, has_ball).
true.
?- T in 0..9, label([T]), time(T, has_ball).
T = 0 ;
T = 1 ;
T = 2 ;
T = 3 ;
T = 4 ;
T = 5 ;
T = 6 ;
T = 7 ;
T = 8 ;
T = 9.
只有在不受限于有限域的时间标签上才会出错,这是很正常的。