Lets do a bit of rephrasing: since you want to use the predicate in more than one instantiation pattern " a procedure that given a value and a list, it deletes all the occurrence of that value in the list" does not define how it should behave in the other cases. So, we probably want something like "the predicate is true if the second and third argument are lists L1, L2 and L1 is the same list with L2 if we ignore all occurrences of the first argument"
Now, there are two ways of writing a predicate with multiple possible instantiations; you can use meta-logical predicates such as var/1
and ground/1
and write code for each one (which will probably allow you to write code that's optimised for that specific instantiation) or write code that would define the properties logically (which can be more challenging).
In this case, we could do something like that:
del(_, [], []).
del(X, [X|L1], L2):-
del(X,L1,L2).
del(X, [H|L1], [H|L2]):-
X\==H,
del(X,L1,L2).
which has the following behaviour:
19 ?- del(1, [1,2,3], X).
X = [2, 3] ;
false.
1,2,3,
20 ?- del(1, [1,2,3], [2,3]).
true ;
false.
21 ?- del(X, [1,2,3], [2,3]).
X = 1 ;
false.
22 ?- del(X, [1,2,3], Y).
X = 1,
Y = [2, 3] ;
X = 2,
Y = [1, 3] ;
X = 3,
Y = [1, 2] ;
Y = [1, 2, 3] ;
false.
23 ?- del(X, P, Y).
P = Y, Y = [] ;
P = [X],
Y = [] ;
P = [X, X],
Y = [] ;
P = [X, X, X],
Y = [] ;
P = [X, X, X, X],
Y = [] ;
P = [X, X, X, X, X],
Y = [] ;
P = [X, X, X, X, X, X],
Y = [] .
regarding the last call; prolog returns a list of X that grows cause a depth-first algorithm is used; by using length/2
we can get the results breadth-first (_G means that the variable is not instantiated (it can be anything)):
24 ?- length(P,N), del(X, P, Y).
P = [],
N = 0,
Y = [] ;
P = [X],
N = 1,
Y = [] ;
P = [_G548],
N = 1,
Y = [_G548] ;
P = [X, X],
N = 2,
Y = [] ;
P = [X, _G551],
N = 2,
Y = [_G551] ;
P = [_G548, X],
N = 2,
Y = [_G548] ;
P = [_G548, _G551],
N = 2,
Y = [_G548, _G551] ;
P = [X, X, X],
edit: as @chac pointed out, the predicate above behaves incorrectly if the first list has (at least) one duplicate element:
?- del(X,[1,2,1],Y).
X = 1,
Y = [2] ;
X = 2,
Y = [1, 1] ;
X = 1,
Y = [1, 2] ; <----- wrong
Y = [1, 2, 1].
this is because \==/2
and \=/2
do not actually impose a restriction on the variable.
this could be solved by switching the order of the rules in the third clause:
del(_, [], []).
del(X, [X|L1], L2):-
del(X,L1,L2).
del(X, [H|L1], [H|L2]):-
del(X,L1,L2),
X\==H.
4 ?- del(X,[1,2,1],Y).
X = 1,
Y = [2] ;
X = 2,
Y = [1, 1] ;
Y = [1, 2, 1] ;
false.
this however means that the predicate is no longer tail-recursive. to fix that we could keep a list of the values that X shouldn't be:
del(X,L1,L2):-
del(X,L1,L2,[]).
del(X, [], [], NotX):-
\+ member(X,NotX).
del(X, [X|L1], L2, NotX):-
del(X,L1,L2,NotX).
del(X, [H|L1], [H|L2], NotX):-
X\==H, % <--- optional; stops the execution earlier (saving time)
del(X,L1,L2,[H|NotX]).
however, according to the following, the tail recursive version is actually slower:
?-time(forall((between(1,50,N),length(X,N),del2(1,X,[2,3,2,3])),true)).
% 25,600,793 inferences, 5.468 CPU in 5.548 seconds (99% CPU, 4682134 Lips)
true.
?- time(forall((between(1,50,N),length(X,N),del_tr(1,X,[2,3,2,3])),true)).
% 37,346,143 inferences, 6.426 CPU in 6.428 seconds (100% CPU, 5811563 Lips)
true.
still, the + - + doesnt work (it falls in a infinite loop). but why? the problem lies in the order of the clauses:
del(1, L1, [2]) will first apply the rule that "adds" X to the head of L1, then applies the same rule forever.
this can be countered by using (again) length/2
:
?- length(X,2), del(1,X,[2]).
X = [1, 2] ;
X = [2, 1] ;
false.
alternatively we can change the order of the clauses:
del(_, [], []).
del(X, [H|L1], [H|L2]):-
X\==H,
del(X,L1,L2),
X\==H.
del(X, [X|L1], L2):-
del(X,L1,L2).
yet length/2
might be useful again since without it prolog does a depth first search:
?- del(1,X,[2]).
X = [2] ;
X = [2, 1] ;
X = [2, 1, 1] ;
X = [2, 1, 1, 1] ;
X = [2, 1, 1, 1, 1] ;
X = [2, 1, 1, 1, 1, 1] ;
X = [2, 1, 1, 1, 1, 1, 1]
of course length/2
can be incorporated in a wrapper predicate since it doesnt affect the other instantiation patterns.
delMember(Y, [1,2,3,1,2,3], X)
, I get only one solution (Y=1, X=[2,3,2,3]). Why I don't get also Y=2 and Y=3 ? – Proliferation