如何在 Prolog 中定义新运算符?

Yue*_*ang 6 operators prolog

我试图定义一个运算符=>>来检查它的一个操作数是否是另一个操作数的两倍。

我到目前为止尝试过:

:- 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)

问题是什么?我该如何解决?

fal*_*lse 5

有几个问题。首先,为如此小的任务定义一个操作符有点大材小用。始终牢记声明运算符的成本:每次定义运算符时,都会稍微更改语言,这意味着阅读该程序文本的人也必须学习该语法。

因此最好只使用简单的谓词名称。如果您确实坚持这样做,请尝试以类似于现有运算符的方式使用运算符。根据优先级,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)


mat*_*mat 4

这是一个确定性问题:可能有进一步的解决方案((;)/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)