Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I define a new operator in Prolog?

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?

like image 456
Yue Wang Avatar asked Aug 07 '15 05:08

Yue Wang


2 Answers

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.
like image 99
mat Avatar answered Sep 18 '22 03:09

mat


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.
like image 26
false Avatar answered Sep 19 '22 03:09

false