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

How can I define a new operator in Prolog?

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

到目前为止我试过了:

:- op(200, xfy, =>>).

=>>(L, R) :- double(L, R); double(R, L). 

double(L, R) :- L is R * 2.

但是在 RPEL 中使用时,我得到了:

?- (-8) =>> (-4).
true ;
false. 
%^^^^ note here

?- 7 =>> 3.
false.

?- 40 =>> 20.
true ;
false.
%^^^^ note here

?- 20 =>> 40.
true.

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

这是一个确定性问题:可能有进一步的解决方案((;)/2可以读作“”),因此Prolog回溯(并且找不到替代方案).

有一个简单的方法可以解决这个问题:使用 once/1 提交第一个解决方案,如果有的话:

L =>> R :- once((double(L, R) ; double(R, L))). 

另请注意,在这种情况下,您可能希望使用 =:=/2,而不是 is/2。更好的是,如果你正在处理整数,只需使用 CLP(FD) 约束,你的谓词将是确定性的 更一般:

:- use_module(library(clpfd)).

L =>> R :- L #= R*2 #\/ R #= L*2.

示例:

?- 40 =>> 20.
true.

?- 40 =>> X, X #< 80.
X = 20.

?- X =>> Y, X #= 2, Y #= 3.
false.

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

所以最好只使用一个简单的谓词名称。如果您真的坚持这样做,请尝试以类似于现有运算符的方式使用运算符。我们在ISO Prolog中按照优先级大致分为以下三组:

  • 1200-900:规则,控制结构。最值得注意的连词是 1000.

  • 700,xfx:比较相关的中缀运算符,如:= \= == \== @< @=< @> @>= =.. is =:= =\= < =< > >=。请注意,这些都是非关联的,因为嵌套并不特定于它们的含义。

  • 500-200: 表达式。

另请注意,所有对称关系都有对称名称 - 除了否定关系:\=\==

:- op(700,xfx,=:*:=).

=:*:=(X, Y) :-
   (X - 2*Y) * (Y - 2*X) =:= 0.

以下可能更可取,因为中间结果更小,因此乘法更便宜并且永远不会产生溢出:

=:*:=(X, Y) :-
   sign(X - 2*Y) * sign(Y - 2*X) =:= 0.