Hello,
I received “The Craft of Prolog” a couple of days ago, and I started giving a look at chapter one yesterday. It’s really an interesting book, there are many observations that let the reader pay attention to the “little details”.
One of the things it starts with is the predicate member/2, which is one of the really basic ones, but even there there are some interesting observations to make.
- in most languages there is a clear and sharp distinction between inputs and outputs, what a function expects to receive and what it returns. In Prolog the distinction is not so clear, many predicates are bidirectional, so to say, and depending on what you want to do, you can use them either in a way or in another
- this holds true for the member/2 predicate as well. The predicate is defined by two clauses:
member(X, [X|_]).
member(X, [_|T]) :-
member(X, T).
Two clauses: a base case and a recursive step. Either something is the head of a list (and then you’re done, that something is a member of the list), or it isn’t (and then you have to take a recursive step and see if you find it in the tail). Because one is dealing with a recursive data type (a list) all seems clear and no problematic at all. But actually this definition has some further implications:
As O’Keefe says, anything that matches the head of first clause, will also match the head of the second, this will create a choice point: even if an element is found in the list’s head that won’t mean that the predicate is finished with its job. If one is using it in an interactive environment, he’ll get a “true” as an answer, but the cursor will stay there blinking, waiting for a full stop or a semicolon. This is not what one would expect from a “normal” member function: as soon as you hit what you wanted to check, you exit the computation and return True. There’s nothing to wait for.
So you think: ok, I might use a cut (!) to prune the alternatives, and rewrite the first clause like this:
member(X, [X|_]) :- !.
this gives you a behavior which is more similar to what one would get in a more standard programming language, but this prevents from using member/2 to extract all values of the list.
If you submit a query with the first argument instantiated, then you’re using the predicate to check if a value is in the list:
member(a, [a, s, d])
(the funny thing is that if the value is found more than once in the list you’re searching, and you force backtracking by adding a semicolon, you’ll get “true” multiple times and you start thinking “ok, but this is counting, not checking whether something is a member of a list” (and you might think member is not exactly the right name for the predicate).
?- member(a, [a, s, d, a, f, a]).
true ;
true ;
true.
But you can submit a query with an uninstantiated variable, in that case the predicate will extract all values, if you force backtracking by hitting the semicolon:
?- member(X, [a, s, d, a, f, a]).
X = a ;
X = s ;
X = d ;
X = a ;
X = f ;
X = a.
Adding a cut in the first clause wouldn’t allow for this use of the predicate. So wouldn’t one want two distinct predicates maybe? A member with cut for the usual checking mechanism, and a backtrackable version to generate all values…