40
votes

The problem that I face, is a bit trivial. I want to use logical not in Prolog, but it seems that not/1 is not the thing that I want:

course(ai).
course(pl).
course(os).

have(X,Y) :- course(X),course(Y),not(X = Y).

I query:

have(X,Y), write(X-Y), nl , fail.

And I do not get the result I want :(

5
what is the result that you want?Thanos Tintinidis
what is the result that you got would also be nice for question readability btwm09
I want it to print for me combinations of the course names, where the two are not equal, I mean: ai-pl ai-os pl-ai pl-os os-ai os-plMasood Delfarah
@MasoodDelfarah That's exactly what it does, isn't it?sepp2k
then use \+ (X = Y) or X \= Y instead.m09

5 Answers

42
votes

In place of not(X = Y) you need to write \+ X = Y or X \= Y. But consider to use dif(X,Y) instead. dif/2 is present in B, SWI, YAP, SICStus. To see the difference:

?- X = b, dif(a, X).
X = b.

?- X = b, \+ a = X.
X = b.

So up to now everything seems to be fine. But what, if we simply exchange the order of the two goals?

?- \+ a = X, X = b.
false.

?- dif(a, X), X = b.
X = b.

(\+)/1 now gives us a different result, because there is an answer for a = X, the goal \+ a = X will fail.

(\+)/1 is thus not negation, but means not provable at this point in time.

A safe approximation of dif/2 is possible in ISO Prolog, too.

14
votes

In both SWI-Prolog and GNU Prolog, the following should work:

have(X, Y) :- course(X), course(Y), X \= Y.

In SWI-Prolog, you can also use dif/2, which can be more convenient since you can use it earlier in the predicate:

have(X, Y) :- dif(X, Y), course(X), course(Y).
6
votes

As an adjunct to the answer by user "false" above, i.e.

"In place of not(X = Y) you need to write \+ X = Y,"

This might give the impression that:

a. "not" and "\+" are different things

b. The \+ will work whereas the not will, err, not.

My understanding is that "not" and "\+" are equivalent, but that \+ is preferred in modern Prolog programs, because it conveys a sense that is more intuitive. Specifically, whereas "not" might suggest "is not true" to the unwary coder, "\+" suggests "is not provable" which is much closer to the truth of what that operation is really saying. In Prolog, the "not" is an example of "negation as failure", but it is felt that \+ will make it clearer to the programmer just what precisely is being asserted in any given rule. So you CAN use "not" (most PL implementations keep it for backwards-compatibility) but to be an idiomatic modern PL programmer, you probably should prefer to use \+.

3
votes

Reading Samuel Kamin's book on chapter 8, prolog, I found this solution that also fits here and explains how to use the cut:

The cut is a way of giving the programmer additional control over the computation by allowing him to indicate places where backtracking is impermissible. Specifically, the cut is written as an exclamation mark (!) occurring as a goal in the right-hand side of a clause such as:

G :- H, !, R.

Suppose this clause is chosen to satisfy a goal g with which G unifies. An attempt is made to satisfy H. If successful, R is proven. If the proof of R succeeds, then g is proven; in this case, the cut has no part to play. If, however, the proof of R fails, rather than backtrack and try to re-prove H, the presence of the cut causes the goal g to fail immediately; this occurs even if there are further clauses that might apply to g. An example is the definition of not-equals:

equals(X, X).
not-equals(X, Y) :- equals(X, Y), !, fail.
not-equals(X, Y).

not-equals(X, Y) should succeed if X is not equal to Y, and fail if it is; X and Y should be bound to ground expressions (i.e. have no free variables) when attempting to satisfy this goal.

1
votes

Like you said, OP, this is trivial.

Try

course(ai).
course(pl).
course(os).

have(X,Y) :- course(X), course(Y), X \== Y).

That should fix your predicate.

Looking one step ahead though, mathematically phrasing, you are probably looking for the solution to (n C 2) as opposed to (n P 2) which your predicate currently provides—combination instead of permutation, choice of selection as opposed to arrangements of choices of selection. This is what I think.

If this is what you want, I would suggest you try

course(ai).
course(pl).
course(os).

have(X,Y) :- course(X), course(Y), X @< Y).

Which would prevent duplicate reversed results.

@< means atomically less than. < is for integers, @< is for atoms.