2
votes

Getting started with prolog and I am trying to do some simple exercises, however I got stuck pretty early... What I am trying to 'say' is: mike likes anyone if it's a man or a woman and it is not mike. But the X \= mike seems to be ignored:

man(mike).
man(danny).
man(samuel).
man(henry).

woman(samantha).
woman(jane).
woman(betty).
woman(jenny).

likes(mike, X) :-
    man(X);
    woman(X),
    X \= mike.

mike shows up...

?- findall(X, likes(mike, X), L).
L = [mike, danny, samuel, henry, samantha, jane, betty, jenny].

I assume it's because prolog evaluates the rule man(X) first so mike is true. However if I change it to:

likes(mike, X) :-
    X \= mike,
    man(X);
    woman(X).

I will only get the women.

?- findall(X, likes(mike, X), L).
L = [samantha, jane, betty, jenny].

The question is how to do it then? Thanks!
Note: I am using swi-prolog

3
Because of operator precedence, man(X); woman(X), X \= mike. is interpreted as man(X) ; (woman(X), X \= mike).. You can change that with parentheses, to group the ;: (man(X) ; woman(X)), X \= mike.. That's also one of the problems with your second example. See information on operator precedence. - lurker
Say listing(likes)! It shows you the problem immediately. - false
The title is a little strange. Actually (\=)/2 is not a rule, but a predicate. And this predicate is used in a literal inside a rule. - user502187

3 Answers

1
votes

The problem is operator precedence. Just C/C#/Java/SQL and other procedural languages, in Prolog, logical OR (;) has a different precedence than does logical AND (',').

In most procedural languages, an expression like

A || B && C

Is parsed as

A || ( B && C )

In Prolog, an expression like yours

A ; B , C    

is parsed as if written

A ; (B,C)

So your

likes(mike, X) :-
  man(X);
  woman(X),
  X \= mike.

is essentially

likes(mike,X) :- man(X) ; ( woman(X) , X \= mike ) .

You are asserting that Mike likes anyone who is

  • a man (including himself), OR
  • a woman who is not mike.

When what you mean was that Mike likes anyone — male or female — other than himself.

So...you need to make the precedence explicit with parentheses,

likes(A,B) :- ( man(B) ; woman(B) ) , A \= B .

or better yet, break your predicate up into 2 clauses and make it generic:

likes(A,B) :- man(B)   , A \= B .
likes(A,B) :- woman(B) , A \= B .

Improve things further by making gender an attribute of the entity (a person) rather than a fact in and of itself:

person( mike     , male ) .
person( danny    , male ) .
person( samuel   , male ) .
person( henry    , male ) .
person( samantha , female ) .
person( jane     , female ) .
person( betty    , female ) .
person( jenny    , female ) .

Then likes/2 is even simpler, since one can like people of both genders:

likes(A,B) :- person(B,_) , A \= B .
1
votes

Besides the OR-problem there is another problem. The predicate (\=)/2 is not a constraint, means its essentially not a fully declarative predicate.

The predicate is usually bootstrapped via negation as failure, and negation as failure itself is not fully declarative.

The predicate is bootstrapped from ordinary unification as follows:

X \= Y :- \+ X = Y.

An alternative definition would be:

X \= X :- !, fail.
_ \= _.

If you really want to be able to move around the inequality you should take a Prolog system with constraints and then resort to dif/2.

With dif/2 you can write:

likes(mike, X) :-
    (dif(X, mike),
     man(X)
  ;  woman(X)).

Or the following:

likes(mike, X) :-
    (man(X),
     dif(X, mike)
  ;  woman(X)).

And you will get the same results. This is not possible with (\=)/2.

Bye

P.S.: Reasons for the problems with (\=)/2. Negation as failure \+ A sometimes not only acts as ~A, but also as ~exists X1,..,Xn A. The interested reader might like to wade through the admittedly little old, but still applicable:

The Proof Theory of Logic Programs with Negation
Robert Stärk, Bern, 1992
http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.29.2745&rep=rep1&type=pdf

0
votes

The problem is with the OR operator (;) :

man(mike).
man(danny).
man(samuel).
man(henry).
woman(samantha).
woman(jane).
woman(betty).
woman(jenny).

Your solution:

likes(mike, X) :- (man(X);woman(X)),X\=mike.

Another solution (using an equivalent to the OR operator) could be:

likes(mike, X) :- man(X), X\=mike.
likes(mike, X) :- woman(X).

Notice that you don't need an OR operator.