Prolog bind variable to opposite of another boolean variable

103 Views Asked by At

I want to create a clause that will hold if its two boolean parameters are equal and the third parameter is 1 or its two boolean parameters are not equal and the third parameter is 0. My first attempt:

equal_bools(X, Y, N) :-
    X = Y,
    N=1.
equal_bools(X, Y, N) :-
    \+ X = Y,
    N=0.

This seems to work:

?- equal_bools(true, true, 0).
false.

?- equal_bools(true, true, 1).
true.

?- equal_bools(true, false, 0).
true.

?- equal_bools(true, false, 1).
false.

The problem is, I really need Y to be bound to the correct value. For example, equal_bools(false, Y, 0). should give back Y = true .. Instead:

?- equal_bools(false, Y, 0).
false.

It seems \+ X = Y checks for equality, but doesn't bind X or Y to any values. How do I fix this? This is for learning purposes, so I'd like to understand exactly how to do this rather than get a built-in clause that does all this or another library.

3

There are 3 best solutions below

1
brebs On BEST ANSWER

Nicely deterministic (i.e. no unwanted choicepoints), using (as usual) first-argument indexing, and unification:

bool_opp(true, false).
bool_opp(false, true).

% Unifies X with Y
bools_int(1, X, X) :-
    bool_opp(X, _).
bools_int(0, X, Y) :-
    bool_opp(X, Y).

Results in swi-prolog:

?- bools_int(I, X, Y).
I = 1,
X = Y, Y = true ;
I = 1,
X = Y, Y = false ;
I = 0,
X = true,
Y = false ;
I = 0,
X = false,
Y = true.

?- bools_int(1, X, Y).
X = Y, Y = true ;
X = Y, Y = false.

?- bools_int(0, X, Y).
X = true,
Y = false ;
X = false,
Y = true.

?- bools_int(0, X, true).
X = false.

?- bools_int(1, X, true).
X = true.

?- bools_int(1, X, false).
X = false.

?- bools_int(0, X, false).
X = true.

?- bools_int(I, X, false).
I = 1,
X = false ;
I = 0,
X = true.
3
Marijn On

Prolog uses the principle of negation as failure. This means that a negation succeeds if it is not possible to make the negated thing true.

In this case it is possible to make false = Y true, by unifying Y to false (in other languages you would say to assign Y the value false). So \+ X = Y can never be true when either X or Y or both are (unbound) variables.

To put it another way: Prolog does not try to find a value for Y to make the negation true, instead it checks if it is possible to make the equality true, and if it is possible then the negation is false.

Furthermore Prolog does not have booleans as a data type. You can see the list of types at https://www.swi-prolog.org/pldoc/man?section=typetest, boolean is not one of them. true and false are just atoms, like abc. Therefore Prolog does not do any boolean logic with true and false, like (not true) equals false or anything like that.

If you want to implement this you need to provide the boolean logic yourself, for example as follows:

reverse_bool(true, false).
reverse_bool(false, true).

equal_bools(X, X, 1).
equal_bools(X, Y, 0) :-
    reverse_bool(X, Y).

Note that I simplified the case of N = 1 by turning the rule into a fact with the equality of X and Y expressed by using the same variable X and putting N = 1 as a constant in the third argument. Similarly N = 0 is expressed as a constant in the rule for when X is not equal to Y.

3
slago On

I think the simplest way to achieve the desired result is as follows:

equal_bools(false, false, 1).
equal_bools(false, true,  0).
equal_bools(true,  false, 0).
equal_bools(true,  true,  1).