我试图定义一个运算符=>>来检查它的一个操作数是否是另一个操作数的两倍。
我到目前为止尝试过:
:- op(200, xfy, =>>).
=>>(L, R) :- double(L, R); double(R, L).
double(L, R) :- L is R * 2.
Run Code Online (Sandbox Code Playgroud)
但是当在 RPEL 中使用时,我得到了:
?- (-8) =>> (-4).
true ;
false.
%^^^^ note here
?- 7 =>> 3.
false.
?- 40 =>> 20.
true ;
false.
%^^^^ note here
?- 20 =>> 40.
true.
Run Code Online (Sandbox Code Playgroud)
问题是什么?我该如何解决?
有几个问题。首先,为如此小的任务定义一个操作符有点大材小用。始终牢记声明运算符的成本:每次定义运算符时,都会稍微更改语言,这意味着阅读该程序文本的人也必须学习该语法。
因此最好只使用简单的谓词名称。如果您确实坚持这样做,请尝试以类似于现有运算符的方式使用运算符。根据优先级,ISO Prolog 中大致分为以下三组:
1200-900:规则、控制结构。最值得注意的是合数是 1000。
700, xfx: 比较相关的中缀运算符,例如:= \= == \== @< @=< @> @>= =.. is =:= =\= < =< > >=。请注意,这些都是非关联的,因为嵌套并不特定于它们的含义。
500-200:表达式。
另请注意,所有对称关系都有对称名称 - 除了否定关系:\=和\==。
:- op(700,xfx,=:*:=).
=:*:=(X, Y) :-
(X - 2*Y) * (Y - 2*X) =:= 0.
Run Code Online (Sandbox Code Playgroud)
以下可能更可取,因为中间结果较小,因此乘法更便宜并且不会产生溢出:
=:*:=(X, Y) :-
sign(X - 2*Y) * sign(Y - 2*X) =:= 0.
Run Code Online (Sandbox Code Playgroud)
这是一个确定性问题:可能有进一步的解决方案((;)/2可以读为“或”),因此 Prolog 回溯(并且找不到替代方案)。
有一个简单的方法可以解决此问题:用于once/1提交第一个解决方案(如果有):
L =>> R :- once((double(L, R) ; double(R, L))).
Run Code Online (Sandbox Code Playgroud)
另请注意,在这种情况下,您可能想要使用=:=/2,而不是is/2。更好的是,如果您正在处理整数,只需使用 CLP(FD) 约束,您的谓词将是确定性的并且更加通用:
:- use_module(library(clpfd)).
L =>> R :- L #= R*2 #\/ R #= L*2.
Run Code Online (Sandbox Code Playgroud)
例子:
?- 40 =>> 20.
true.
?- 40 =>> X, X #< 80.
X = 20.
?- X =>> Y, X #= 2, Y #= 3.
false.
Run Code Online (Sandbox Code Playgroud)