7

I have a standard procedure for determining membership of a list:

member(X, [X|_]).  
member(X, [_|T]) :- member(X, T).

What I don't understand is why when I pose the following query:

?- member(a,[a,b]).

The result is

True;  
False.

I would have thought that on satisfying the goal using the first rule (as a is the head of the list) True would be returned and that would be the end of if. It seems as if it is then attempting to satisfy the goal using the second rule and failing?

Prolog interpreter is SWI-Prolog.

false
  • 10,264
  • 13
  • 101
  • 209
Justin
  • 322
  • 3
  • 13

3 Answers3

7

Let's consider a similar query first: [Edit: Do this without adding your own definition ; member/2 is already defined]

?- member(a,[b,a]).
   true.

In this case you get the optimal answer: There is exactly one solution. But when exchanging the elements in the list we get:

?- member(a,[a,b]).
   true
;  false.

Logically, both are just the affirmation that the query is true.

The reason for the difference is that in the second query the answer true is given immediately upon finding a as element of the list. The remaining list [b] does not contain a fitting element, but this is not yet examined. Only upon request (hitting SPACE or ;) the rest of the list is tried with the result that there is no further solution.

Essentially, this little difference gives you a hint when a computation is completely finished and when there is still some work to do. For simple queries this does not make a difference, but in more complex queries these open alternatives (choicepoints) may accumulate and use up memory.

Older toplevels always asked if you want to see a further solution, even if there was none.

Edit:

The ability to avoid asking for the next answer, if there is none, is extremely dependent on the very implementation details. Even within the same system, and the same program loaded you might get different results. In this case, however, I was using SWI's built-in definition for member/2 whereas you used your own definition, which overwrites the built-in definition.

SWI uses the following definition as built-in which is logically equivalent to yours but makes avoiding unnecessary choice points easier to SWI — but many other systems cannot profit from this:

member(B, [C|A]) :-
   member_(A, B, C).

member_(_, A, A).
member_([C|A], B, _) :-
   member_(A, B, C).

To make things even more complex: Many Prologs have a different toplevel that does never ask for further answers when the query does not contain a variable. So in those systems (like YAP) you get a wrong impression.

Try the following query to see this:

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

SWI is again able to determine that this is the only answer. But YAP, e.g., is not.

false
  • 10,264
  • 13
  • 101
  • 209
  • I actually still get the true and false answer for both of those queries. In the second query I dont see why it would continue trying to examine [b] when the first rule has already found a as the head of the list. – Justin Jan 30 '13 at 02:09
  • @Justin Becasue you pressed the `;` button. It examined the head, found it matches, and reported back `true` to you. But it still hasn't looked at the rest of the list. – Will Ness Jan 30 '13 at 02:12
  • @WillNess so your saying even though rule 1 has satisfied the query, rule 2 will be evaluated (and therefore examining the tail of the list [b] and failing)? – Justin Jan 30 '13 at 02:24
  • I can see now that I am running trace that what your saying is correct, It attempts to test `member(a, [b])` and fails. Can anyone explain why it does this? Am I correct in saying that when matching a goal to the head of a rule, it will return when the goal is satisfied, but then still test other rules with a matching head when pressing ';'? – Justin Jan 30 '13 at 02:37
  • @Justin When Prolog succeeds in satisfying a query it does not stop, but pauses. That is how Prolog is defined. It pauses, remembers where it's at, prints the results and waits for an operator's command: a ';', or 'Space', 'Enter', '?' etc. – Will Ness Jan 30 '13 at 02:39
  • I also got true and false for both of those queries. And I think it's expected behaviour, because in `member(a,[b,a]).` after finding the first "true" there is still an empty list to check. – Sergii Dymchenko Jan 30 '13 at 02:53
  • @WillNess thanks for clarifying. Maybe you can answer one more question. As an experiment, I removed the second rule, turned on trace, and gave a query that could be satisfied by rule 1. To my surprise after returning true and pausing, I pushed ';' and the same rule is evaluated a second time but fails. I understand why prolog might pause to allow the user to opt to search for more solutions, but why evaluate the same rule twice? – Justin Jan 30 '13 at 02:55
  • @Justin make a clean `pl` file with the relevant code and test it again. If it behaves in the same manner, then please post this code to some codepaste site (hpaste.org is very light and fast), together with the copy of your interaction session. – Will Ness Jan 30 '13 at 11:07
  • @Justin: Sorry for the confusion, I have added now some clarification. – false Jan 30 '13 at 12:55
2

Are you using the ";" operator after the first result then pushing return? I believe this is asking the query to look for more results and as there are none it is coming up as false.

Dan
  • 21
  • 1
  • Yes I am. However my experience so far is that normally when posing a query which only has one possible result it only prints True and then returns to the ?- prompt. The ";" operator is normally available only when there is multiple answers. – Justin Jan 30 '13 at 01:54
1

Do you know about Prolog's cut - !?

If you change member(X, [X|_]). to member(X, [X|_]) :- !. Prolog will not try to find another solution after the first one.

Sergii Dymchenko
  • 6,890
  • 1
  • 21
  • 46
  • Without a cut it behaves basically as you described in the question - "then attempting to satisfy the goal using the second rule and failing". – Sergii Dymchenko Jan 30 '13 at 02:42
  • 2
    I see. So basically if you know that an earlier rule is 'the best solution' you can add a cut. The downside being you will never know if another interesting solution existed. – Justin Jan 30 '13 at 02:45
  • 1
    `member(X,[1,2,3])` should give three answers/solutions not one. – false Jan 30 '13 at 12:19
  • @Justin: Introducing cut in Prolog programs is extremely hairy. Many attempts as the one above go wrong for some cases. So better avoid the cut in the beginning, there is enough to learn from cut free programs. – false Jan 30 '13 at 14:25