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

时间:2015-08-07 05:37:35

标签: 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 个答案:

答案 0 :(得分:3)

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

最好的方法就是保留一个简单的谓词名称。如果你真的坚持它,尝试以类似于现有运营商的方式使用运营商。我们根据优先级在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.

答案 1 :(得分:2)

这是一个决定论问题:可能还有其他解决方案((;)/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.