Search code examples
prolog

(SWI)Prolog: Order of sub-goals


I have two, slightly different, implementations of a predicate, unique_element/2, in Prolog. The predicate succeeds when given an element X and a list L, the element X appears only once in the list. Below are the implementations and the results:

Implementation 1:

%%% unique_element/2
unique_element(Elem, [Elem|T]) :-
    not(member(Elem, T)). 
unique_element(Elem, [H|T]) :-
    member(Elem, T), 
    H\==Elem, 
    unique_element(Elem, T), 
    !. 

Results:

?- unique_element(X, [a, a, b, c, c, b]). 
false.

?- unique_element(X, [a, b, c, c, b, d]).
X = a ;
X = d.

Implementation 2:

%%% unique_element/2
unique_element(Elem, [Elem|T]) :- 
    not(member(Elem, T)). 
unique_element(Elem, [H|T]) :-
    H\==Elem, 
    member(Elem, T), 
    unique_element(Elem, T), 
    !. 

In case you didn't notice at first sight: H\==Elem and member(Elem, T) are flipped on the 2nd impl, rule 2.

Results:

?- unique_element(X, [a, a, b, c, c, b]).
X = a.

?- unique_element(X, [a, b, c, c, b, d]).
X = a ;
X = d.

Question: How does the order, in this case, affect the result? I realize that the order of the rules/facts/etc matters. The two specific rules that are flipped though, don't seem to be "connected" or affect each other somehow (e.g. a cut in the wrong place/order).

Note: We are talking about SWI-Prolog here.

Note 2: I am aware of, probably different and better implementations. My question here is about the order of sub-goals being changed.


Solution

  • TL;DR: Read the documentation and figure out why:

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

    I wonder why you stop so close from figuring it out yourself ;-)

    There are too many ways to compare things in Prolog. At the very least, you have unification, which sometimes can compare, and sometimes does more; than you have equvalence, and its negation, the one you are using. So what does it do:

    ?- a \== b. % two different ground terms
    true.
    
    ?- a \== a. % the same ground term
    false.
    

    Now it gets interesting:

    ?- X \== a. % a free variable and a ground term
    true.
    
    ?- X \== X. % the same free variable
    false.
    
    ?- X \== Y. % two different free variables
    true.
    

    I would suggest that you do the following: figure out how member/2 does its thing (does it use unification? equivalence? something else?) then replace whatever member/2 is using in all the examples above and see if the results are any different.

    And since you are trying to make sure that things are different, try out what dif/2 does. As in:

    ?- dif(a, b).
    

    or

    ?- dif(X, X).
    

    or

    ?- dif(X, a).
    

    and so on.

    See also this question and answers: I think the answers are relevant to your question.

    Hope that helps.