如何正确地为 findall/3 制定例外?

How to formulate an exception for findall/3 correctly?

我开始学习 Prolog,我想获得 findall/3 玩家的对手列表。在泛化中,我只想将实际上是玩家的对手添加到列表中,除了我要求自己的玩家。我该如何制定这个例外?我知道否定是失败的概念,但我不确定我是否以及如何在这里需要它。

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].

我实际上预计,该决议将按如下方式运作:

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

我错过了什么? 非常感谢!

代码:

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).

运行它:

预计不会:

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

预计:

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

那么 "not expected" 情况下 Who = irina 会发生什么:

  • findall/3 尝试收集满足 opponent(Who,O)
  • 的所有值 Y
  • 通过事实找到antonmariamichal
    • opponent(irina, anton).
    • opponent(irina, maria).
    • opponent(irina, michael).
  • 它还会尝试规则 opponent(X, Y) :- ...X = irina(仅一次)。但是测试 X \= Y 失败 因为 X 已经被约束为 irinaY 仍然是新鲜的(完全不受约束)。这意味着如果尝试 XY 的统一可能会成功(这是正在测试的内容,= 并不意味着 "is not equal")。所以 X \= Y 在这个计算点是错误的。如果您将 X \= Y 移到 player(Y) 之后,那么 Y 将被限制为从 player/1 事实中获取的任何内容,这将简化为值的实际比较XY.