Reputation: 348
I would like to implement a simple if and only if relation in my Prolog code. Here is a simple example of what I would like to have:
?- a_iff_b(a, b).
true.
?- a_iff_b(x, x).
true.
?- a_iff_b(a, x).
false.
?- a_iff_b(x, b).
false.
You get the point—the predicate should be true if either the first argument is a
and the second argument is b
or if neither is the first argument a
nor is the second b
. I hope that makes sense. Mathematically speaking, X = a <==> Y = b
.
Here is my attempt:
a_iff_b(X, Y) :-
X = a, Y = b;
X \= a, Y \= b.
But this code introduces a choice point:
?- a_iff_b(a, b).
true ;
false.
Why? It seems that the following works, I mean it does not introduce the choice point:
a_iff_b(X, Y) :- (X = a -> Y = b; Y \= b).
However, I find this a little less readable, especially because in my actual predicate, the two sides of the equivalence are more complex (the are also predicates).
Upvotes: 0
Views: 411
Reputation: 9378
Your code introduces a choice point because it contains a choice introduced by the ;
operator. You might think of this as a "logical or" operator, and somewhat informally it is. But procedurally it is an "introduce a choice point here, and on backtracking, explore the second branch as well" operator.
It's true that a Sufficiently Powerful Prolog Compiler™ might be able to recognize that the two branches here are mutually exclusive, but it appears that your Prolog system isn't that powerful. I would be at least mildly surprised if any Prolog you can get for free would be able to do this without a choice point. Especially if your actual conditions are more complex, as you say.
If you want this to be more readable, some tips:
In general, never use ;
at the end of a line as if it were just a variant of ,
. It's too easy to miss it, since the general expectation is that lines end in ,
. Try to format your code in some way that really makes the ;
stick out (see examples below).
In general, if you want to use ;
(not _ -> _ ; _
), especially as the single top-level goal in a clause, consider using separate clauses instead:
a_iff_b(a, b).
a_iff_b(X, Y) :-
X \= a,
Y \= b.
In any case, all of the following are possible ways of cutting away the choice:
a_iff_b_1(a, b) :-
!.
a_iff_b_1(X, Y) :-
X \= a,
Y \= b.
a_iff_b_2(X, Y) :-
( X = a, Y = b
-> true
; X \= a, Y \= b ).
a_iff_b_3(X, Y) :-
( X = a, Y = b,
!
; X \= a, Y \= b ).
a_iff_b_4(X, Y) :-
( X = a, Y = b
; X \= a, Y \= b ),
!.
a_iff_b_5(X, Y) :-
once(( X = a, Y = b
; X \= a, Y \= b )).
Upvotes: 3