Vladimir
Vladimir

Reputation: 225

Use of OR in Prolog

I want to write the following as a fact in Prolog using the format want_to_eat(Jack, food): Jack wants to eat food A OR food B. I tried to write it as want_to_eat(Jack, (A ; B)), but want_to_eat(Jack, A) returns false. What else can I do?

Follow-up question: Suppose Jack wants to eat apple, and at least one of bread and cherry. It seems one cannot distinguish And and Or if one uses the following.

wants_to_eat(jack, apple).
wants_to_eat(jack, bread).
wants_to_eat(jack, cherry).

What should I do in this case?

Upvotes: 1

Views: 172

Answers (1)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 476574

First of all there is a problem with the syntax: an uppercase identifier is a variable, not a constant. So you should only write:

wants_to_eat(jack, apple).

not:

wants_to_eat(Jack, Apple).

Next the ; in an a parameter is just a functor. Prolog does not attach any semantics to these functors. For Prolog (;)/2 is just (;)/2. The same happens for a comma: wants_to_eat(jack, (apple, apple, apple)) does not "fold" to the fact that jack will eat an apple, it is just a chain of (,)/2 functors. Only in the body of a clause, it is seen as a "logical or" (although in Prolog, most operators have a slightly different meaning than their logical counterpart, especially the not is a tricky one).

If you write:

wants_to_eat(jack, (apple; bread; cherry)).

then it only unify with the entire second parameter:

?- wants_to_eat(jack, A).
A =  (apple;bread;cherry).

or due to unification, you can unify with a part of it:

?- wants_to_eat(jack, (A;_)).
A = apple.

?- wants_to_eat(jack, (_;B)).
B =  (bread;cherry).

So we can not just write such values in a semi-colon separated list. Normally what one does is write several facts, like:

wants_to_eat(jack, apple).
wants_to_eat(jack, bread).
wants_to_eat(jack, cherry).

We can also perform unification in the body, and then use a semi-colon, like:

wants_to_eat(jack, F) :-
    F = apple;
    F = bread;
    F = cherry.

The above might not be very feasible in case the are a large amount of people (alice, bob, jack, etc.), and a large number of foods (apple, bread, cherry), and we want to define a cross product: so all these people like all these foods. In that case we could for example write it like:

wants_to_eat(P, F) :-
    (
        P = alice;
        P = bob;
        P = jack
    ), (
        F = apple;
        F = bread;
        F = cherry
    ).

But the above is rather ugly. We might solve this by writing two lists, and use the member/2 [swi-doc] instead:

wants_to_eat(P, F) :-
    member(P, [alice, bob, jack]),
    member(F, [apple, bread, cherry]).

The above can also be applied to jack only:

wants_to_eat(jack, F) :-
    member(F, [apple, bread, cherry]).

Upvotes: 2

Related Questions