Search code examples
prolog

Why member/2 does not return immediately


I need a predicate for whether an element is in the list. I've tried to use member/2, but I've noticed a strange behaviour.

When I call something like member(1, [1, 2, 3]). the SWI-prolog outputs true and waits for me to press enter before execution terminates. This does not happen when element is not in list.

I have two questions:

  1. Why is this happening?
  2. What would be the code for member/2 which will return immediately?

Solution

    1. Why is this happening?

    The definition of member/2 permits to succeed several times, even if the very same solution is found. In your list, all elements were different. But consider the case with two additional occurrences of 1 at the end:

    ?- member(1,[1,2,3,1,1]).
       true
    ;  true
    ;  true.
    
    1. What would be the code for member/2 which will return immediately?

    This is commonly called memberd/2.

    ?- memberd(1,[1,2,3,1,1]).
       true.
    
    ?- memberd(X,[1,2,3,1,1]).
       X = 1
    ;  X = 2
    ;  X = 3
    ;  false.
    

    The difference is easy to grasp. member/2 is essentially defined as:

    member(X, [X|_Xs]).
    member(E, [_X|Xs]) :-
       member(E, Xs).
    

    Note that these two clauses are not mutually exclusive! This is the reason why it searches for alternate solutions even if a solution was already found. In the rule, the E and the _X might be identical.

    By ensuring that E and X are different in the rule, we get a first definition of memberd/2:

    memberd(X, [X|_Xs]).
    memberd(E, [X|Xs]) :-
       dif(E, X),
       memberd(E, Xs).
    

    Thanks to this dif/2 the rule is only considered, if the current element is different to E. What is missing here, however, is that Prolog will still have to consider both before concluding that they are disjoint.

    The full definition contains some technicalities to avoid useless choices.

    memberd(X, [E|Es]) :-
       if_(X = E, true, memberd(X, Es)).
    

    Using library(reif) for SICStus and SWI this is expanded to:

    memberd(X, [E|Es]) :-
        (   X\=E
        ->  memberd(X, Es)
        ;   X==E
        ->  true
        ;   X=E,
            true
        ;   dif(X, E),
            memberd(X, Es)
        ).