(SWI)Prolog: Order of sub-goals

H\==Elem is testing for syntactic inequality at the point in time when the goal is executed. But later unification might make variables identical:

?- H\==Elem, H = Elem.
H = Elem.

?- H\==Elem, H = Elem, H\==Elem.
false.

So here we test if they are (syntactically) different, and then they are unified nevertheless and thus are no longer different. It is thus just a temporary test.

The goal member(Elem, T) on the other hand is true if that Elem is actually an element of T. Consider:

 ?- member(Elem, [X]).
 Elem = X.

Which can be read as

(When) does it hold that Elem is an element of the list [X]?

and the answer is

It holds under certain circumstances, namely when Elem = X.

If you now mix those different kinds of goals in your programs you get odd results that can only explained by inspecting your program in detail.

As a beginner, it is best to stick to the pure parts of Prolog only. In your case:

  • use dif/2 in place of \==

  • do not use cuts - in your case it limits the number of answers to two. As in unique_element(X, [a,b,c])

  • do not use not/1 nor (\+)/1. It produces even more incorrectness. Consider unique_element(a,[a,X]),X=b. which incorrectly fails while X=b,unique_element(a,[a,X]) correctly succeeds.


Here is a directly purified version of your program. There is still room for improvement!

non_member(_X, []).
non_member(X, [E|Es]) :-
   dif(X, E),
   non_member(X, Es).

unique_element(Elem, [Elem|T]) :- 
    non_member(Elem, T). 
unique_element(Elem, [H|T]) :-
    dif(H,Elem), 
     % member(Elem, T),          % makes unique_element(a,[b,a,a|Xs]) loop
    unique_element(Elem, T).

?- unique_element(a,[a,X]).
   dif(X, a)
;  false.              % superfluous

?- unique_element(X,[E1,E2,E3]).
   X = E1,
   dif(E1, E3),
   dif(E1, E2)
;  X = E2,
   dif(E2, E3),
   dif(E1, E2)
;  X = E3,
   dif(E2, E3),
   dif(E1, E3)
;  false.

Note how the last query reads?

When is X a unique element of (any) list [E1,E2,E3]?

The answer is threefold. Considering one element after the other:

X is E1 but only if it is different to E2 and E3

etc.


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.


Can you not define unique_element like tcount Prolog - count repetitions in list

unique_element(X, List):- tcount(=(X),List,1).

Tags:

Prolog