0

i was trying to implement a prolog rule which removes the number X from a certain list and returns the list without the X . However after getting the correct result i get as an alternative the original list as a result as well and i get figure out where is the problem in my code.

remove1(X,[],[]).
remove1(X,[X|T],T).
remove1(X,[H|T],[H|R]):-
    X \= H,
    remove1(X,T,R).
remove1(X,[H|T],[H|T]):-
    \+member(X,T).
Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
marwagaser
  • 67
  • 1
  • 1
  • 9

2 Answers2

1

Let's answer that with an example. Say we remove 2, from [1,2,3]. Then Prolog will evaluate it like this:

remove1(2,[1,2,3],R):
    fail. % clause 1
    fail. % clause 2
    2 \= 1, remove1(2,[2,3],R'): % clause 3
        fail. % clause 1
        R = [1,3] % clause 2
        2 \= 2... fail. % clause 3
        \+member(2,[3]),R=[1,2,3] % clause 4
    \+member(2,[2,3])... fail. % clause 4

(answers put in boldface)

So once Prolog has found the first answer, it backtracks, checks if 2 is a member of the tail T=[3] (which it is not), and thus replies (based on the last clause) that [1,2,3] is an answer as well.

It makes me wondering why you added the last clauses. If 2 cannot be found, Prolog would have answered the list anyway. So the program:

remove1(X,[],[]).
remove1(X,[X|T],T).
remove1(X,[H|T],[H|R]):-
    X \= H,
    remove1(X,T,R).

Would be sufficient.

If you however want the query to succeed only if it has deleted one occurrence, you should remove the first query as well. So:

remove1_force(X,[X|T],T).
remove1_force(X,[H|T],[H|R]):-
    X \= H,
    remove1(X,T,R).

Both predicates will only remove the first occurrence of X in the list. So remove1_force(1,[1,2,3,1,2,3],R). Will only give one answer: R = [2,3,1,2,3].

Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
1

Basically, the first and the last clause are unnecessary. The last clause is where the wrong results come from.

This seems to be another "textbook" predicate, and it is usually called select/3. The textbook definition in [Sterling&Shapiro], page 67, is identical to the SWI-Prolog library implementation and goes something like this:

%!  select(?Elem, ?List1, ?List2)
%
%   Is true when List1, with Elem removed, results in List2.

select(X, [X|Tail], Tail).
select(Elem, [Head|Tail], [Head|Rest]) :-
    select(Elem, Tail, Rest).

You will notice that it does not need any helper predicates and is a true relation. Try for example:

?- select(X, [1,2,3], R).

or even:

?- select(X, List, Rest), numbervars(List).

(numbervars/1 just makes the solutions a bit more readable)

This definition is as general as it is because... well, it is a general definition. The first clause does the "selecting", the second clause defines the recursive step.

[Sterling&Shapiro]: Sterling L, Shapiro EY. The art of Prolog: advanced programming techniques. MIT press; 1994.