Prolog 当在条件的头部使用整型暂停时,将如何处理整型暂停

Prolog 当在条件的头部使用整型暂停时,将如何处理整型暂停,prolog,eclipse-clp,Prolog,Eclipse Clp,对于两个变量A和B,我有以下条件: [A,B] #:: 1..10, (A #= 3) or (B #= 3), ((A #> 3 or B #>3) -> % expression 1 ; % expression 2 ) %cntd 问题在第2行,解算器不知道A和B的值,如何在不指定第2行变量值的情况下决定继续执行哪一个条件分支 合理的做法是,当解算器遍历变量的可能值时,根据变量的值决定此分支。 但是,正如我所发现的,在知道变量的值之前,它会经过这些表

对于两个变量
A
B
,我有以下条件:

[A,B] #:: 1..10,
(A #= 3) or (B #= 3),
((A #> 3 or B #>3) ->
     % expression 1
;
     % expression 2
)
%cntd
问题在第2行,解算器不知道
A
B
的值,如何在不指定第2行变量值的情况下决定继续执行哪一个条件分支

合理的做法是,当解算器遍历变量的可能值时,根据变量的值决定此分支。
但是,正如我所发现的,在知道变量的值之前,它会经过这些表达式中的一个。有什么办法可以防止这种情况发生?

问题出在哪里? 重要注意事项是在if-else中使用
->
(箭头)。当我们有表达式
S->T;U
,将对
S
进行评估,如果它包含一些变量,可能会对代码产生一些副作用。为了更清楚,请尝试运行一些示例:

?-[A,B] #:: 1..10,
(A #= 3) or (B #= 3),
((A #> 3 or B #>3) ->
    write("expression 1")
;
    write("expression 2")
).
由于未确定
A
B
的值,因此该条件始终为真,并且将打印
表达式1
的值。此外,结果是:

A = A{1 .. 10}
B = B{1 .. 10}
There are 6 delayed goals.
Yes (0.00s cpu)
A = 3
B = B{4 .. 10}
Yes (0.00s cpu)
No (0.00s cpu)
正如您所看到的,
A
B
的边界不会改变,因为它被挂起到将来的表达式中,而正如我们所看到的,边界不会改变

现在尝试不同的示例:

?- [A,B] #:: 1..10,
(A #= 3) or (B #= 3),
((A #> 3 or B #>3) ->
    write("expression 1")
;
    write("expression 2")
),
A = 3. % this line is added
由于
A
的值已确定
A#>3
不正确,但是
B
呢?这等于3还是大于3?正如我们所说,条件部分将被执行!因此,
B
的最后一个约束是
B#>3
。除了执行
write(“表达式1”)
,结果是:

A = A{1 .. 10}
B = B{1 .. 10}
There are 6 delayed goals.
Yes (0.00s cpu)
A = 3
B = B{4 .. 10}
Yes (0.00s cpu)
No (0.00s cpu)
最后一个例子是:

?- [A,B] #:: 1..10,
(A #= 3) or (B #= 3),
((A #> 3 or B #>3) ->
    write("expression 1")
;
    write("expression 2")
),
A = 3,
B = 3. % this line is added
在本例中,还打印了表达式1,结果为:

A = A{1 .. 10}
B = B{1 .. 10}
There are 6 delayed goals.
Yes (0.00s cpu)
A = 3
B = B{4 .. 10}
Yes (0.00s cpu)
No (0.00s cpu)
这是因为将执行箭头和始终
表达式1

一个解决方案是使用
操作员喜欢以下内容:

[A,B] #:: 1..10, 
(
    (A = 3, B = 3, write('expression 21'))
    ; 
    (A = 3, B #> 3, write('expression 11'))
    ; 
    (A #> 3, B #> 3, write('expression 12'))
    ; 
    (A #> 3, B = 3, write('expression 13'))
), 
A = 3,
B = 5.
上述代码的结果是:

A = 3
B = 5
Yes (0.00s cpu, solution 1, maybe more)
它会打印:

expression 21 expression 11

这意味着第一个分支失败,但它失败并自动回溯,然后转到下一个案例!随着下一个案例的应用,一切都会成功

有什么问题吗?
重要注意事项是在if-else中使用
->
(箭头)。当我们有表达式
S->T;U
,将对
S
进行评估,如果它包含一些变量,可能会对代码产生一些副作用。为了更清楚,请尝试运行一些示例:

?-[A,B] #:: 1..10,
(A #= 3) or (B #= 3),
((A #> 3 or B #>3) ->
    write("expression 1")
;
    write("expression 2")
).
由于未确定
A
B
的值,因此该条件始终为真,并且将打印
表达式1
的值。此外,结果是:

A = A{1 .. 10}
B = B{1 .. 10}
There are 6 delayed goals.
Yes (0.00s cpu)
A = 3
B = B{4 .. 10}
Yes (0.00s cpu)
No (0.00s cpu)
正如您所看到的,
A
B
的边界不会改变,因为它被挂起到将来的表达式中,而正如我们所看到的,边界不会改变

现在尝试不同的示例:

?- [A,B] #:: 1..10,
(A #= 3) or (B #= 3),
((A #> 3 or B #>3) ->
    write("expression 1")
;
    write("expression 2")
),
A = 3. % this line is added
由于
A
的值已确定
A#>3
不正确,但是
B
呢?这等于3还是大于3?正如我们所说,条件部分将被执行!因此,
B
的最后一个约束是
B#>3
。除了执行
write(“表达式1”)
,结果是:

A = A{1 .. 10}
B = B{1 .. 10}
There are 6 delayed goals.
Yes (0.00s cpu)
A = 3
B = B{4 .. 10}
Yes (0.00s cpu)
No (0.00s cpu)
最后一个例子是:

?- [A,B] #:: 1..10,
(A #= 3) or (B #= 3),
((A #> 3 or B #>3) ->
    write("expression 1")
;
    write("expression 2")
),
A = 3,
B = 3. % this line is added
在本例中,还打印了表达式1,结果为:

A = A{1 .. 10}
B = B{1 .. 10}
There are 6 delayed goals.
Yes (0.00s cpu)
A = 3
B = B{4 .. 10}
Yes (0.00s cpu)
No (0.00s cpu)
这是因为将执行箭头和始终
表达式1

一个解决方案是使用
操作员喜欢以下内容:

[A,B] #:: 1..10, 
(
    (A = 3, B = 3, write('expression 21'))
    ; 
    (A = 3, B #> 3, write('expression 11'))
    ; 
    (A #> 3, B #> 3, write('expression 12'))
    ; 
    (A #> 3, B = 3, write('expression 13'))
), 
A = 3,
B = 5.
上述代码的结果是:

A = 3
B = 5
Yes (0.00s cpu, solution 1, maybe more)
它会打印:

expression 21 expression 11

这意味着第一个分支失败,但它失败并自动回溯,然后转到下一个案例!随着下一个案例的应用,一切都会成功

只要您坚持纯逻辑,约束编程就很适合Prolog。但是,正如您所演示的,不能将诸如cut(!)if-then-else(->)等程序元素与约束逻辑自由混合

使用if-then-else或cuts仅当条件在“约束设置时间”中包含(即无条件为真)或解除(无条件为假)时才是安全的。实际上,这意味着此类条件不应包含问题变量(域变量等),而应仅包含先验已知的问题参数(常量)。专用的建模语言区分了这两个方面,但Prolog没有

如何在约束模型中不表达备选方案

上述情况意味着您不能使用cut/if-then-else来表达您想要表达的备选方案。简单地摆脱条件的承诺选择方面,并将其重写为一个纯粹的析取,可能很有诱惑力。例如,您可以重写

( Usage #>= 1000 -> Rate#=7, Bonus#=100              % WRONG
;                   Rate#=9, Bonus#=0
)
作为一个纯粹的析取

( Usage #>= 1000, Rate#=7, Bonus#=100                % INEFFICIENT
; Usage #<  1000, Rate#=9, Bonus#=0
)

这是可行的,但只有在A和B都实例化后才会起作用。如本例所示,如果条件是可恢复的,我们可以做得更好:

choice(A, B) :-
    Bool #= (A#>3 or B#>3),
    delayed_choice(Bool).

delay delayed_choice(Bool) if var(Bool).
delayed_choice(1) :- write("expression 1").
delayed_choice(0) :- write("expression 2").
当域满足条件时,此操作已起作用:

?- choice(A, B), B #> 3.
expression 1
将常规析取转化为约束

ECLiPSe有一个漂亮的特性,称为 . 通过使用简单的注释,这可以有效地将Prolog析取转化为约束。从上面正确但低效的公式开始,我们可以写:

?- ( Usage #>= 1000, Rate#=7, Bonus#=100
   ; Usage #<  1000, Rate#=9, Bonus#=0
   ) infers most.

Usage = Usage{-1.0Inf .. 1.0Inf}
Rate = Rate{[7, 9]}
Bonus = Bonus{[0, 100]}
There is 1 delayed goal.
Yes (0.00s cpu)
?-(使用率=1000,费率=7,奖金=100
使用率<1000,费率=9,奖金=0
)推断得最多。
用法=用法{-1.0Inf..1.0Inf}
速率=速率{[7,9]}
奖金=奖金{[01100]}
有一个延迟的目标。
是(0.00s cpu)
正如
Rate
Bonus
的域所示,甚至在确定适用的替代方案之前,已经从析取中提取了有用的信息。

约束