How to express a disjunction of inequalities compactly without redundant answers/solutions
Asked Answered
O

4

19

Consider what I have tried:

dif_to_orto(A, B, C) :-
   (  dif(A, B)
   ;  dif(A, C)
   ).

While this definition is fine from a declarative viewpoint it contains many redundancies. Think of:

?- dif_to_orto(A, B, C), A = 1, B = 2, C = 2.
   A = 1, B = 2, C = 2
;  A = 1, B = 2, C = 2.   % unexpected redundant solution

And not even in this case:

?- dif_to_orto(A, B, C), A = 1, B = 2, C = 3.
   A = 1, B = 2, C = 3
;  A = 1, B = 2, C = 3.   % unexpected redundant solution

At least, here is a case without redundancy...

?- dif_to_orto(A, B, C), A = 1, B = 2, C = 1.
   A = 1, B = 2, C = 1
;  false.                 % unexpected inefficient leftover choicepoint

...but with a resource-wasting leftover choicepoint.

Rare are the occasions where this definition is efficient:

?- dif_to_orto(A, B, C), A = 1, B = 1, C = 2.
   A = 1, B = 1, C = 2.

Also that the most general query produces two answers sounds very inefficient to me:

?- dif_to_orto(A, B, C).
   dif:dif(A,B)
;  dif:dif(A,C).

... which produces also the following redundancy:

?- dif_to_orto(1, B, B).
   dif:dif(1,B)
;  dif:dif(1,B).    % unexpected redundant answer

One dif/2 would be enough!

Is there a way to avoid all these redundancies and inefficiencies?

Offensive answered 5/2, 2021 at 10:2 Comment(2)
aren't the duplicate solutions due to B being equal to C? what about having dif(B,C) in the second case?Colley
@coredump: Added two cases for clarification.Offensive
S
16

How about this one:

dif_to_orto(A, B, C) :-
   dif(A-A, B-C).

The test cases:

?- dif_to_orto(A, B, C), A = 1, B = 2, C = 2.
A = 1,
B = C, C = 2.

?- dif_to_orto(A, B, C), A = 1, B = 2, C = 3.
A = 1,
B = 2,
C = 3.

?- dif_to_orto(A, B, C), A = 1, B = 2, C = 1.
A = C, C = 1,
B = 2.

?- dif_to_orto(A, B, C), A = 1, B = 1, C = 2.
A = B, B = 1,
C = 2.

?- dif_to_orto(A, B, C).
dif(f(B, A), f(A, C)).

?- dif_to_orto(1, B, B).
dif(B, 1).
Sogdiana answered 5/2, 2021 at 21:25 Comment(5)
@Offensive I'm puzzled. Did I do anything wrong? Should I retreat this answer?Sogdiana
No, looks like a perfect answerOffensive
dif_to_orto(1, B, B). is another nice caseOffensive
In SICStus, the two last calls give different results: prolog:dif(A-A,B-C) and prolog:dif(1-1,B-B) respectively. So the dif/2 goal is not "simplified". I think the behavior is ultimately the same, but I thought it was worth noting it...Headless
@jnmonette: SICStus shows the original goal which is at least in the first case much more intuitive than what you get in SWI with its out-of-nowhere structure f/2. In the second case, SWI is in fact nicer.Offensive
K
8

This solution first awaits for 2 of the 3 variables to be comparable, then if it cannot decide whether the constraint should succeed adds a new constraint:

dif_to_orto(A, B, C) :-
    when((?=(A, B) ; ?=(A, C) ; ?=(B, C)),
         (   ?=(A, B) ->
              ( A\==B ->  true ; dif(A, C) )
         ;
             (
                ?=(A, C) ->
                 ( A\==C -> true ; dif(A, B) )
             ;
                 ( B\==C -> true ; dif(A, B) )
             )
         )).

Sample runs:

?- dif_to_orto(A, B, C), A = 1, B = 2, C = 2.
A = 1,
B = C, C = 2.

?- dif_to_orto(A, B, C), A = 1, B = 2, C = 3.
A = 1,
B = 2,
C = 3.

?- dif_to_orto(A, B, C), A = 1, B = 2, C = 1.
A = C, C = 1,
B = 2.

?- dif_to_orto(A, B, C).
when((?=(A, C);?=(B, C);?=(A, B)),  (?=(A, B)->(A\==B->true;dif(A, C));?=(A, C)->(A\==C->true;dif(A, B));B\==C->true;dif(A, B))).

?- dif_to_orto(1, 2, Z).
true.

?- dif_to_orto(1, B, B).
dif(B, 1).

Reversing the checks:

dif_to_orto(A, B, C) :-
    when((?=(A, B) ; ?=(A, C) ; ?=(B, C)),
         (
           A==B -> dif(A, C)
           ;
           ((A==C ; B==C) -> dif(A, B) ; true)
         )).
Keystroke answered 5/2, 2021 at 19:0 Comment(2)
hehe, you mean to add the third comparison... will check itKeystroke
@false: added the other branch.Keystroke
H
6

Here is one suggestion. As far as I can tell, it does not create choice points or redundant solutions:

dif_to_orto(A, B, C) :-
   when(?=(A,B),(A==B->dif(A,C);true)),
   when(?=(A,C),(A==C->dif(A,B);true)).

For each disjunct, wait until it is known to be true or false. Once known, check its truth and, if false, then post the other disjunct.

Headless answered 5/2, 2021 at 14:21 Comment(2)
dif_to_orto(1, 2, Z). gives when(?=(1, Z), (1==Z->dif(1, 2);true)). How can I be sure that this is not tantamount to false?Offensive
BTW, if you have another solution, put it into another answer, so we can vote, accept and bounty them one-by-one.Offensive
K
5

Expanding on the definition of dif/2:

dif_to_orto(A, B, C):-
   when((?=(A,B), ?=(A, C)), (A \== B -> true ; A \== C)).

Sample runs:

?- dif_to_orto(A, B, C), A = 1, B = 2, C = 2.
A = 1,
B = C, C = 2.

?- dif_to_orto(A, B, C), A = 1, B = 2, C = 3.
A = 1,
B = 2,
C = 3.

?- dif_to_orto(A, B, C), A = 1, B = 2, C = 1.
A = C, C = 1,
B = 2.

?- dif_to_orto(A, B, C).
when((?=(A, B), ?=(A, C)),  (A\==B->true;A\==C)).
Keystroke answered 5/2, 2021 at 18:5 Comment(8)
dif_to_orto(1, 2, Z). has some constraint attached to Z. Why? How can I be sure that it is irrelevant.Offensive
This answer delays the evaluation of the constraint until all three variables can be syntactically compared so in your example I only see that a manual inspection of the suspended goal determines it will succeed. Maybe delaying the evaluation until the three of them are comparable is not the best idea then...Keystroke
Try another one.Offensive
BTW, syntax is here SWI-specificOffensive
@false: do you mean using when/2 and dif/2 ? I don't see them in GProlog. I don't have a license for Sicstus so I am not really familiar with their counterparts.Keystroke
Just a missing pair of round brackets around the if-then-else.Offensive
Or, (in SWI), say listing(dif_to_orto)Offensive
I see it from the last sample run, the if-then-else prints surrounded with round brackets. Fixed codeKeystroke

© 2022 - 2024 — McMap. All rights reserved.