I'm trying to define an operator =>>
that checks if one of its operands is double of the other operand.
I tried so far:
:- op(200, xfy, =>>).
=>>(L, R) :- double(L, R); double(R, L).
double(L, R) :- L is R * 2.
But when used in RPEL, I got :
?- (-8) =>> (-4).
true ;
false.
%^^^^ note here
?- 7 =>> 3.
false.
?- 40 =>> 20.
true ;
false.
%^^^^ note here
?- 20 =>> 40.
true.
What is the problem? How can I fix it?
This is a determinism issue: There may be further solutions ((;)/2
can be read as "or"), and therefore Prolog backtracks (and finds no alternative).
There is an easy way to fix this: Use once/1
to commit to the first solution, if any:
L =>> R :- once((double(L, R) ; double(R, L))).
Notice also that you may want to use =:=/2
, not is/2
, in this case. Even better, if you are working over integers, simply use CLP(FD) constraints, and your predicate will be deterministic and much more general:
:- use_module(library(clpfd)).
L =>> R :- L #= R*2 #\/ R #= L*2.
Examples:
?- 40 =>> 20.
true.
?- 40 =>> X, X #< 80.
X = 20.
?- X =>> Y, X #= 2, Y #= 3.
false.
There are several issues. First, defining an operator for such a tiny task is a bit of an overkill. Always keep in mind the cost of declaring an operator: Every time you define an operator you change the language a bit which means that people who read that program text will have to learn that syntax as well.
So best would be to just stay with a simple predicate name. And if you really insist on it, try to use operators in a way, similar to existing operators. We have roughly the following three groups in ISO Prolog according to their priority:
1200-900: Rules, control constructs. Most notably conjunction is 1000.
700, xfx
: Comparison related infix operators like: = \= == \== @< @=< @> @>= =.. is =:= =\= < =< > >=
. Note that these are all non-associative, since nesting is not specific to their meaning.
500-200: Expressions.
Also note that all the symmetric relations have symmetric names — except for the negated ones: \=
and \==
.
:- op(700,xfx,=:*:=).
=:*:=(X, Y) :-
(X - 2*Y) * (Y - 2*X) =:= 0.
The following might be preferable since the intermediary results are smaller and thus multiplication is cheaper and never produces an overflow:
=:*:=(X, Y) :-
sign(X - 2*Y) * sign(Y - 2*X) =:= 0.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With