0

I have startet to learn Prolog and I want to get a list of opponents of players with findall/3. In the Generalization I just want to add only the opponents to the list who are actually players, except the player I am asking for itself. How can I formulate this exception? I know about the negation as failure concept but I am not sure if and how I need it here.

player(irina).
player(anton).
player(michael).

opponent(irina, anton).
opponent(irina, maria).
opponent(irina, michael).

% Only opponents who are also players should be added to the list, 
% except the player of interest itself 
opponent(X, Y) :- X \= Y, player(Y).

% the query
?- findall(O, opponent(irina,O) , OpponentList).

% the result
% only the first three results from the facts, 
% the results from the rule, a second entry of
% anton and michael are missing.
OpponentList = [anton, maria, michael].

I actually expected, that the resolution would work as follows:

opponent(irina, irina) :- irina \= irina, player(irina).
%                              false          true
%                                      false
% false, hence not added to the list

opponent(irina, anton) :- irina \= anton, player(anton).
%                              true          true
%                                     true
% true, hence added to the list

What am I missing? Many thanks in advance!

false
  • 10,264
  • 13
  • 101
  • 209
Carsten H
  • 831
  • 15
  • 32

1 Answers1

0

Code:

player(irina).
player(anton).
player(michael).

opponent(irina, anton).
opponent(irina, maria).
opponent(irina, michael).

opponent(X, Y) :- 
   format("Called with X = ~q, Y = ~q\n",[X,Y]),
   X \= Y,     % this will fail if Y is still fresh because X and Y can be unified!
   player(Y).

opponent_mod(X, Y) :-
   format("Called with X = ~q, Y = ~q\n",[X,Y]),
   player(Y),  % this will set Y to various players  
   X \= Y.     % the test for un-unifiability of two set vars may or may not succeed 

% query helpfully as code:

q(Who,OpponentList) :- findall(O, opponent(Who,O) , OpponentList).

q_mod(Who,OpponentList) :- findall(O, opponent_mod(Who,O) , OpponentList).

% even better

q_mod_set(Who,OpponentList) :- setof(O, opponent_mod(Who,O) , OpponentList).

Run it:

Not expected:

?- q(irina,X).
Called with X = irina, Y = _19654
X = [anton, maria, michael].

Expected:

?- q_mod(irina,X).
Called with X = irina, Y = _20568
X = [anton, michael].

So what happens in the "not expected" case with Who = irina:

  • findall/3 tries to collect all values Y such that opponent(Who,O)
  • It finds anton, maria, michal through the facts
    • opponent(irina, anton).
    • opponent(irina, maria).
    • opponent(irina, michael).
  • It also tries the rule opponent(X, Y) :- ... with X = irina (once only). But the test X \= Y fails because X has been constrained to be irina but Y is still fresh (completely unconstrained). This means that a unification of X and Y could succeed if it were attempted (which is what is being tested, = does not as such mean "is not equal"). So X \= Y is false at this point of computation. If you move X \= Y after player(Y), then Y will have been constrained to whatever was grabbed from the player/1 facts, and this would reduce to an actual comparison of the values of X and Y.
David Tonhofer
  • 14,559
  • 5
  • 55
  • 51
  • Many thanks! I wasn´t aware that the order is important. – Carsten H Apr 14 '20 at 16:39
  • 1
    @CarstenH Order is often important because there is a specific way in how predicates are called: top-down, then left-to-right. Efficiency, and evidently termination are dependent on correct ordering, the cut "!" is based on ordering. Working toward a Logic Program that does not depend on order is a good goal though. For database operations, this means another, reduced, language: Datalog. Prolog has `dif` and `freeze` which allows the Prolog to put predicates "on hold" until more is known about the variables involved. And then there is Constraint Logic Programming for integers, see "CLP(FD)". – David Tonhofer Apr 14 '20 at 16:51