Prolog: Removing Duplicates - prolog

I am trying to remove duplicate entries from a list in prolog. So a list [a,b,a,c,b,a] would return [a,b,c]. I can not use any built in functions. I searched here and found this code.
member(X,[X|_]) :- !.
member(X,[_|T]) :- member(X,T).
set([],[]).
set([H|T],[H|Out]) :- not(member(H,T)), set(T,Out).
set([H|T],Out) :- member(H,T), set(T,Out).
But that would take my list and return [c,b,a] not [a,b,c]
I have remove code that will take an element and a list and return a list with occurrences of that element in the list removed. So I tried to incorporate that into my remove duplicate method but I don't really understand prolog very well so it is not working. Logically I want to take a list cons the head with the recursive call on the new list minus all occurrences of the head. This is what the code would look like in sml.
fun remv(_,nil) = nil
| remv(a,x::xs) = if x=a then remv(a,xs) else x::remv(a,xs);
fun remvdub (nil) = nil
| remvdub(x::xs) = x::remvdub(remv(x,xs));
So this is what I tried in prolog
remv(_,[],[]).
remv(X,[X|T],Ans) :- remv(X,T,Ans).
remv(X,[H|T],[H|K]) :- remv(X,T,K).
remvdub([],[]).
remvdub([H|T],[H|Ans]) :- remvdub(Ans1,Ans), remv(H,T,Ans1).
What am I missing?

% An empty list is a set.
set([], []).
% Put the head in the result,
% remove all occurrences of the head from the tail,
% make a set out of that.
set([H|T], [H|T1]) :-
remv(H, T, T2),
set(T2, T1).
% Removing anything from an empty list yields an empty list.
remv(_, [], []).
% If the head is the element we want to remove,
% do not keep the head and
% remove the element from the tail to get the new list.
remv(X, [X|T], T1) :- remv(X, T, T1).
% If the head is NOT the element we want to remove,
% keep the head and
% remove the element from the tail to get the new tail.
remv(X, [H|T], [H|T1]) :-
X \= H,
remv(X, T, T1).

The snippet of Prolog code that you posted is logically correct. If you would like to keep the first, as opposed to the last, copy of each duplicated item, you can change your code as follows:
member(X,[X|_]) :- !.
member(X,[_|T]) :- member(X,T).
set(A,B) :- set(A, B, []).
set([],[],_).
set([H|T],[H|Out],Seen) :- not(member(H,Seen)), set(T,Out, [H|Seen]).
set([H|T],Out, Seen) :- member(H,Seen), set(T,Out,Seen).
The idea is to add a third parameter, which represents the list of items that you have seen so far, and check the membership against it, rather than checking the membership against the remaining list. Note that set/2 is added to hide this third argument from the users of your predicate.
Demo on ideone.

Related

Prolog: Appending to a list while reusing the same list(out of local stack error)

I have a predicate that takes a list, for example:
[car(blue,2000), car(blue,1000), car(red,2000), car(red,3000)]
And two criteria , example I need to pick the first blue car, and from the remaining list, the first car that cost 2000. I need to return a list of the selected car, which should be
[car(blue,2000),car(red,2000)]
The predicate that I have returns a out of local stack error. From trace , it seems to say that is because I'm doing append(_8757416, [car(blue,2000)], _8757416) , which I'm not really doing. I thought the two append with a temporary list would avoid this kind of bug. Here is the code, hope it can help to understand better want I'm talking about.
checkCriteria([],_,_).
checkCriteria(_,[],_).
checkCriteria([Criteria|T],Cars, ChosenCar):-
include(
goal([Criteria]),
Cars,
FilteredList),
[H|_] = FilteredList,
append([], ChosenCar, Temp ),
append(Temp, [H], ChosenCar),
delete(H, FilteredList, NewList),
checkCriteria(T,NewList, ChosenCar).
% this predicate deletes an element from a list
delete(Y, [Y], []).
delete(X, [X|L1], L1).
delete(X, [Y|L], [Y|L1]):-delete(X,L,L1).
% this predicate check if a element respect a criteria
goal([],_).
goal([Criteria|Cs],Element) :-
criteria(Criteria,Element),
goal(Cs,Element).
:- dynamic criteria/2.
criteria(blue,(car(Color, _))) :- Color = blue, car(Color, _).
criteria(price,(car(_, Price))) :- Price = 2000, car(_,Price).
:- dynamic car/2.
car(blue,2000).
car(blue,1000).
car(red,2000).
car(red,3000).
How to avoid the out of local stack error?
Here is the command that cause the error :
?- checkCriteria([blue, price], [car(blue,2000), car(blue,1000), car(red,2000), car(red,3000)], X).
I had a try and found a few issues, which I (mostly) fixed below. It works with the example, maybe it helps:
The list ChosenCar was never instantiated and hence the appending to it failed. I added an extra parameter to checkCriteria as an accumulator list to collect the results. This is assigned to ChosenCar in the recursion anchors.
You deleted the car that met the criteria from the FilteredList for the next recusion step, but it should be deleted from Cars, so that the next criteria could be checked with the remaining cars.
Your delete predicate can not handle empty lists, and also checkCriteria assumes every criteria to be met by one of the remaining cars. You may want to look into that as well: Handle empty lists in delete and also the case where FilteredList is empty after the include.
checkCriteria([],_,ACC,ACC).
checkCriteria(_,[],ACC,ACC).
checkCriteria([Criteria|T],Cars, ACC, ChosenCar):-
include(
goal([Criteria]),
Cars,
FilteredList),
[H|_] = FilteredList,
append(ACC, [H], ACC1),
delete(H, Cars, NewList),
checkCriteria(T,NewList, ACC1, ChosenCar).
% this predicate deletes an element from a list
delete(Y, [Y], []).
delete(X, [X|L1], L1).
delete(X, [Y|L], [Y|L1]):-delete(X,L,L1).
% this predicate check if a element respect a criteria
goal([],_).
goal([Criteria|Cs],Element) :- criteria(Criteria,Element), goal(Cs,Element).
:- dynamic criteria/2.
criteria(blue,car(Color, _)) :- Color = blue, car(Color, _).
criteria(price,car(_, Price)) :- Price = 2000, car(_,Price).
:- dynamic car/2.
car(blue,2000).
car(blue,1000).
car(red,2000).
car(red,3000).

adjacent involving first and last element, Prolog

HI I would like to know how a method that finds out if two members of a list in Prolog are adjacent as the catch is that the first and the last elements are checked if they are adjacent something like
(b,c,[b,a,d,c])
would give yes they are adjacent. I already have this code
adjacent(X, Y, [X,Y|_]).
adjacent(X, Y, [_|Tail]) :-
adjacent(X, Y, Tail).
but I do not know how to include the head of the list and the last elments as well being compared for being adjacent. If you are really good maybe you can tell me also how it is possible to make something like this
(c,b,[a,b,c,d])
to be true I mean the elements are adjacent no matter which exactly is first.
You can make use of last/2 predicate [swi-doc] to obtain the last element of the list. But you can not use this in the recursive call, since otherwise it will each element in the list pair with the last element as well.
The trick is to make a helper predicate for the recursive part, and then make the adjacent/3 predicate to call the recursive one you wrote yourself, or one where we match with the last element:
adjacent(X, Y, L) :-
adj(X, Y, L).
adjacent(X, Y, [Y|T]) :-
last(T, X).
adj(X, Y, [X,Y|_]).
adj(X, Y, [_|T]) :-
adj(X, Y, T).
Relations about lists can often be described with a Definite Clause Grammar dcg.
A first attempt might be:
adjacent(A, B, L) :-
phrase(adjacent(A, B), L). % interface to DCG
adjacent(A,B) -->
..., ( [A,B] | [B,A] ), ... .
... --> [] | [_], ... .
Yet, this leaves out cases like adjacent(a,d,[a,b,c,d]). One possibility would be to add another rule, or maybe simply extend the list to be considered.
adjacent(A, B, L) :-
L = [E,_|_],
append(L, [E], M),
phrase(adjacent(A, B), L).

delete all occurences of a term in a list

I know this sounds "duplicate" but please help me out
I have defined three terms as follows:
type([a, b, c, d]:location).
type([coffee, tea, lemonade, water, biscuits]: object).
type([order(object, location)]: order).
I have a piece of code that then generates a list of random orders.
I now need a predicate that deletes all the terms that unify with order(X, a), that is, deletes all the orders that have a as location from that list.
For instance, this is an example of list (printed this way to make it readable):
order(tea,a)
order(tea,b)
order(coffee,b)
order(water,c)
order(lemonade,d)
order(biscuits,a)
order(water,c)
order(tea,c)
order(coffee,d)
order(water,d)
applying such needed predicate my_delete(List, [order(_, a), order(_, b)], Result) would give:
order(water,c)
order(lemonade,d)
order(water,c)
order(tea,c)
order(coffee,d)
order(water,d)
So far I've tried to remove a sublist from the main list, but what it does is just delete a single element for a and a single element for b, not all of them. This is the code for such predicate (thanks also to this reference):
remove_list([], _, []).
remove_list([X|Tail], L2, Result):-
member(X, L2),
!,
remove_list(Tail, L2, Result).
remove_list([X|Tail], L2, [X|Result]):-
remove_list(Tail, L2, Result).
and a query that I tried, but didn't work as expected, was:
remove_list(Input_list, [ordine(_, a), ordine(_, b)], Result).
Notice that I need duplicates, so using sets won't work.
You can use negation \+ to avoid further unification over recursion of your filter list:
remove_list([], _, []).
remove_list([X|Tail], ToDelete, Result):-
(\+( memberchk(X, ToDelete) ) ->
Result=[X|NResult] ;
Result=NResult
),
remove_list(Tail, ToDelete, NResult).
Using exclude/3, which takes a predicate, input list and output list:
rev_memberchk(List, Member) :-
memberchk(Member, List).
my_delete(Input_List, Orders, Result) :-
exclude(rev_memberchk(Orders), Input_List, Result).
Using memberchk/1 rather than member/2 for efficiency; you don't need the bound output, just to know if it can unify. If you also have lambda expressions, this can be turned into a one-liner by removing the need to write a predicate with re-ordered arguments:
my_delete(Input_List, Orders, Result) :-
exclude({Orders}/[X]>>memberchk(X, Orders), Input_List, Result).

Prolog remove function

Im new to prolog and i want to be able to remove two locations from a list. I use this to check my code:
remove(e,[(d,1),(e,2),(e,3),(a,4),(b,5),(c,7)], M2).
But it only removed one out of the two locations (which is e). Can anyone help me? This is the rest of the code.
remove(J, [(J,_)|Tail], Tail).
remove(J, [Head|Tail1], C2) :-
remove(J, Tail1, C2).
if your Prolog has library(lists), you can reuse the 'functional' list manipulation builtins: they due their 'functional' behaviour to the fact they take a predicate as argument, being applied (usually) to each list' element (the builtin is exclude/3)
% this must adapt to your data structure
match_key(K, (K, _)).
remove(Key, List, Rest) :-
exclude(match_key(Key), List, Rest).
test:
?- remove(e,[(d,1),(e,2),(e,3),(a,4),(b,5),(c,7)], M2).
M2 = [ (d, 1), (a, 4), (b, 5), (c, 7)].
I think it's worth studying, because a well crafted library it's an essential part of a good language, and learning the simpler parts will ease your introduction to more advanced ones.
You current version actually gives removes all up to first occurrence of e. Try this:
remove(_, [], []).
% matches if current head element is equals to remove element.
remove(J, [(J,_)|Tail], Res) :- remove(J, Tail, Res).
% matches if current head element IS NOT equal to remove element.
% J \= K checks it.
remove(J, [(K, V) | Tail], [(K, V) | Res]) :-
J \= K, remove(J, Tail, Res).

How do I get every consecutive elements from a list in Prolog

Let say I have a list
[[a,b,c],[d,e,f],[g,h,i]]
I want to get every consecutive element and put it in another predicate.
func(a,b).
func(b,c).
func(d,e).
func(e,f).
func(g,h).
func(h,i).
I already wrote the predicate I want to put in, but I'm having a hard time getting two elements from the list of lists.
You can try :
consecutive(L, R) :-
maplist(create_func, L, RT),
flatten(RT, R).
create_func([A,B], [func(A,B)]) :- !.
create_func([A,B | T], [func(A,B) | R]) :-
create_func([B | T], R).

Resources