Both predicates you presented—code 1
and code 2
—are broken.
And the reason why you did not notice that is: queries.
#1)
Consider the query shown in this previous "answer":
?- remove(x, [x,a,b,c,x,d], R).
R = [a,b,c,d] % okay
Okay? Yes, but there may be more answers. Let's press ;↵!
; R = [a,b,c,x,d] % BAD!
; R = [x,a,b,c,d] % BAD!
; R = [x,a,b,c,x,d] % BAD!
; false. % (no more answers)
These three answers are invalid, as each R
contains some x
.
The bottom like: Don't just look at some query answers. Look at all query answers!
#2)
Prolog programs comprise different kinds of clauses: facts, rules, and queries.
Queries are a very—if not the most—important part of your program.
Queries are both documentation and specification. Most importantly, they enable you to delegate the "mechanical" part of program development to the Prolog processor.
So which queries to write?
Start with the most general query:
?- remove(A,B,C).
Queries you expect to succeed:
?- remove(x,[x,a,x,p],[a,p]). % x is first element
?- remove(x,[a,x,p,x],[a,p]). % x is last element
?- remove(x,[a,x,p],[a,p]).
Queries you expect to fail
?- \+ (remove(x,[a,x,p],Ls), dif(Ls,[a,p])).
Ground queries:
?- remove(x,[],[]).
Non-ground queries (queries with variables):
?- remove(X,[a,b,a,c,d,c,a,b],Xs).
The bottom line:
Without queries you are not writing code, you are only writing text.
#3) Now let's get to fixing code 1
: Look at both recursive clauses!
remove(X,[X|Y],Z) :- remove(X,Y,Z).
remove(X,[F|Y],[F|Z]) :- remove(X,Y,Z).
The first rule relates X
with the first list [X|Y]
. OK!
The second rule does not X
with either [F|Y]
or [F|Z]
. BAD!
By adding a prolog-dif goal dif/2
to the second clause we can build that connection.
#4) Done! Here's the complete code for predicate remove/3
:
remove(_X, [], []).
remove(X, [X|Y], Z) :-
remove(X, Y, Z).
remove(X, [F|Y], [F|Z]) :-
dif(X, F),
remove(X, Y, Z).