我正在尝试定义一个运算符=>>
,用于检查其操作数之一是否是另一个操作数的两倍。
我到目前为止尝试过:
:- 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.
有什么问题?我该如何解决?
答案 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.