Removing duplicates in lists within a list - list

How would I go about removing duplicate elements of a list that is itself within a list? For example [[x,x],[x,y],[y,y,z]] should be returned as [[x],[x,y],[y,z]].

lists_dedupe(Ls, Ds) :-
maplist(list_to_set, Ls, Ds).
e.g. your example:
?- lists_dedupe([[x,x],[x,y],[y,y,z]], R).
R = [[x], [x, y], [y, z]]
Sets are basically lists without duplicates, so this maps 'convert to set' over the input to get the output.

Related

Get every element besides the last from a list in Prolog using append

How do you make a rule that returns all elements besides the last in another list in Prolog using append? I've made the equivalent without append but I can't figure out the append solution
remlast([_],[]).
remlast([H1|T1],[H2|T2]):-
H2=H1,
remlast(T1,T2),
!.
I can get the last element from a list with append with this
mylastAppend(X,List):-
append(_,[X],List),!.
But I can't figure out how to use that in the above example
Use:
list_front(List, Front) :-
append(Front, [_], List).
This will split the list in Front of the list and a one-element list from the back of List.
Your "equivalent without append" is not optimal. This is better: https://stackoverflow.com/a/51291825/
Here is a variation which results in a difference list (which can then be used for e.g. an efficient append):
list_without_last_dl(Lst, LstWithout, LstWithoutTail) :-
Lst = [H|T],
list_without_last_dl_(T, H, LstWithout, LstWithoutTail).
list_without_last_dl_([], _H, W, W).
list_without_last_dl_([H|T], H2, [H2|W], WT) :-
list_without_last_dl_(T, H, W, WT).
Result in swi-prolog:
?- list_without_last_dl([a,b,c], W, WT).
W = [a,b|WT].

Finding common elemnts between two lists occuring at the same positions in Prolog

I am using Prolog and I am trying to find a way to implement a predicate in which it takes as arguments two lists and returns a list containing the common elements between them which are found in the same positions in both lists.
The predicate is as follows:
correct_positions(List1, List2,R): succeeds if R is a list containing the letters that
occur in both List1 and List2 in the same positions.
Example:
?- correct_positions([h,e,l,l,o],[h,o,r,s,e],R).
R=[h];
false.
The letter "h" in the previous example is found in both lists at the 1st position, so it should be added to R(the result list). Both letters "e" and "o" also occur in both lists, but they do not occur at the same positions so they are not added to the result list.
Another example:
?- correct_positions([o,r,l,l,a],[l,o,r,l,a],R).
R=[l,a];
false.
In the previous example, the letter "l" occurs twice in both lists, but only the occurrence in the 4th position will be added to the result(because it is found in the same positions in both lists) as well as the letter "a" which is found in the 5th position in both lists. (Letters "r" and "o" also occur in the two lists. However, they do not occur at the same positions so they are not added to the result list.
Assuming same length input lists, a possible solution is as follows:
correct_positions([], [], []).
correct_positions([X|A], [X|B], [X|C]) :-
correct_positions(A, B, C).
correct_positions([X|A], [Y|B], C) :-
dif(X, Y),
correct_positions(A, B, C).
Examples:
?- correct_positions([h,e,l,l,o], [h,o,r,s,e], R).
R = [h] ;
false.
?- correct_positions([o,r,l,l,a], [l,o,r,l,a], R).
R = [l, a] ;
false.
?- correct_positions([X,r,l,l,a], [l,o,r,l,a], R).
X = l,
R = [l, l, a] ;
R = [l, a],
dif(X, l) ;
false.
Remark To deal with input lists of different lengths, you need to generalize the base case.
correct_positions([], [], []). % lists of the same length
correct_positions([], [_|_], []). % first list is the shortest
correct_positions([_|_], [], []). % first list is the longest
correct_positions([X|A], [X|B], [X|C]) :-
correct_positions(A, B, C).
correct_positions([X|A], [Y|B], C) :-
dif(X, Y),
correct_positions(A, B, C).

lists of lists in prolog

I have a lists which contains 2 lists: the first one is a lists of lists and the second one the same. I want to make a list of lists which contains tuples with three elements. This is my initial list :
[ [ [1 2 3 4 a], [aa, bb], R1], [[ 1 3 4 5 b], [X, Y] , R2]] , [anything]]
I want to obtain this list:
[[a, [aa, bb], R1], [b, [x, y], R2]]
How can I implement a predicate to return me the list above in prolog? I have tried this :
get_game_tiles([[[_,_,_,_,I],[X,Y],R]|Tail], [[Elem1,Elem2|Tail]]) :-
findall(S,
( member(S,[[[_,_,_,_,S], [X,Y], R], [[_,[_]]]]), S = I ),
[[Elem1,_|_]]),
findall([A,B],
( member([A,B] ,[[[_,_,_,_,S], [A,B], R], [[_,[_]]]]),
A = X, B = Y
) ,
[[_,Elem2|_]]),
findall(Z,
( member(Z ,[[[_,_,_,_,S], [A,B], Z], [[_,[_]]]]),
Z = R
),
[[_,_|Tail]]).
Your solution with findall calls is very over-done. There are some issues, such as the following:
member(S,[[[_,_,_,_,S], [X,Y], R], [[_,[_]]]])
and
member(Z ,[[[_,_,_,_,S], [A,B], Z], [[_,[_]]]])
These are a cyclic terms. You're querying whether S is a member of the list, [[[_,_,_,_,S], [X,Y], R], [[_,[_]]]]. S is embedded in a term within an element of the list you are asking if S is a member of. Likewise, you're asking if Z is a member of the list, [[[_,_,_,_,S], [A,B], Z], [[_,[_]]]]. Z is embedded in a term within an element of the list you are asking if Z is a member of.
member([A,B] ,[[[_,_,_,_,S], [A,B], R], [[_,[_]]]])
This isn't so much a problem but will always be false since the list consists of the following elements, neither of which match [A,B] (a list of two elements):
[[_,_,_,_,S], [A,B], R] % a list of three elements
[[_, [_]]] % a list of one element ([_, [_]])
Assuming I understand the conditions of your problem (I am not yet convinced 100% that I do), the approach in Prolog is actually much simpler:
get_game_tiles([], []). % Empty list maps to empty list
get_game_tiles([S|_], []) :- % List starting with non-matching pattern results in
S \= [[_,_,_,_,_], [_,_], _]]. % empty and ends recursion
get_game_tiles([[[_,_,_,_,X], [A,B], R]]|T], [[X, [A,B], R]|TR]) :-
get_game_tiles(T, TR).

How do I delete all but duplicates from a list in prolog

I have a list, let's say X=[a,b,c,d,c,e,d], how can I delete one of each character so the only answer remains X1=[c,d].
I have a bunch of lists with just alphabetical letters and I need a command which would delete every single letter once if list contains such and do nothing if there's none of that letter.
I have tried using selectchk/3 for this but it only works for specific cases.
For instance if I have list X=[a,b,c,d,d,e,e,f,f,g,h],
I can write selectchk(d,X,X1), selectchk(e,X1,X2), selectchk(f,X2,X3) etc.
As I said, this only works for specific case, however if I add general predicate, let's say I have selectchk/3 for every single letter, but, for example,
new list is X1=[a,b,c,d,d] and I use selectchk(f,X,X3),
command fails and doesn't name the next list X3, so the next command which checks for letter 'G' in list X3 can't run since there's no such list. Is there a possibility to do OR command, if one fails?
X=[a,a,c,d,d,e]
selectchk(a,X,X1) OR (if there's no a) append([],X,X1),selectchk(b,X1,X2) OR (if there's no 'b'), append([],X2,X3) etc.
Thanks
There are several ways one could do this. I think selectchk/3 forms a good basis for a solution, which is really to "automate" what you attempted to do manually and use a recursion:
select_each_one(L, R) :-
sort(L, PickList),
select_each_one(L, PickList, R).
select_each_one(L, [H|T], R) :-
selectchk(H, L, R1),
select_each_one(R1, T, R).
select_each_one(L, [], L).
The sort provides a "picklist" containing just one of each of the elements in the original list. The select_each_one/3 predicate then performs the selectchk with each of those elements on each iteration of the original list.
?- select_each_one([a,b,c,d,c,e,d], L).
L = [c, d] ;
false.
?- select_each_one([a,b,c,d,d,e,e,f,f,g,h], L).
L = [d, e, f] ;
false.
Another approach would be to copy the list over one element at a time, but track whether we've seen the element or not:
select_each_one(L, R) :-
select_each_one(L, [], R).
select_each_one([H|T], Seen, R) :-
( member(H, Seen)
-> R = [H|R1], select_each_one(T, Seen, R1)
; select_each_one(T, [H|Seen], R)
).
select_each_one([], _, []).

How do I remove supersets in a list of lists?

Let's say I have the following list:
List = [[a],[a,b],[a,c],[b,c],[b,d],[a,b,c],[a,b,d],[b,c,e],[b,d,e,f]]
The goal is to remove every list in the list that is a superset of a list in the list.
The list that contains the lists always has the following properties:
The lists in the list are sorted by length
Each list in the list is sorted
My initial idea was to simply start with the first list in the list and go through all other lists and remove the lists that are a superset. Next I'd look at the second list, et cetera.
After removing all supersets of the list [a] it should look like this:
List = [[a],[b,c],[b,d],[b,c,e],[b,d,e,f]]
Next the supersets of [b,c] should be removed:
List = [[a],[b,c],[b,d],[b,d,e,f]]
Last is the supersets of [b,d]:
List = [[a],[b,c],[b,d]]
And the line above should be the result.
I already made a predicate akin to the member predicate, but instead of taking a single element and comparing it to the list, this takes an entire list and compares it to the list:
memberList([],_).
memberList([X|Xs],Y) :-
member(X,Y),
memberList(Xs,Y).
This only works with lists.
?- memberList(a,[a,b,c]).
false.
?- memberList([a],[a,b,c]).
true .
?- memberList([a,b],[a,b,c]).
true .
But after this I'm a bit lost.
I tried the following which should remove the supersets of a single set, but it did not work:
removeSupersetsList(_,[],[]).
removeSupersetsList(X,[Y|Ys],[Y|Out]) :-
not(memberList(X,Y)),
removeSupersetsList(X,Ys,Out).
removeSupersetsList(X,[Y|Ys],Out) :-
memberList(X,Y),
removeSupersetsList(X,Ys,Out).
So I was wondering if someone could point me in the right direction to remove all supersets from a list or maybe even give the right predicate.
I'm using SWI-Prolog, where I find a crafted libray for ordered sets, and the required test, then using select/3 it's really easy to sanitize the list
rem_super_sets([], []).
rem_super_sets([L|Ls], R) :-
( select(T, Ls, L1), % get any T in Ls
ord_subset(L, T) % is T a superset of L ?
-> rem_super_sets([L|L1], R) % discard T, keep L for further tests
; R = [L|L2],
rem_super_sets(Ls, L2)
).
here a verification and the result
test :-
List = [[a],[a,b],[a,c],[b,c],[b,d],[a,b,c],[a,b,d],[b,c,e],[b,d,e,f]],
rem_super_sets(List, R),
write(R).
?- test.
[[a],[b,c],[b,d]]
true.
memberList([],_).
memberList([X|Xs],Y) :- member(X,Y),
memberList(Xs,Y).
%remove(ToRemove,ListWithSublists,LocRez,FinalRez)
%A list from ListWithSublists is removed,depending on ToRemove
% LocRez is accumulator used to obtain the FinalRez ( at the end )
remove(_,[],LocRez,LocRez) :- !.
remove(ToRemove,ListWithSublists,LocRez,FinalRez) :- ListWithSublists=[Sublist|Rest],
memberList(ToRemove,Sublist),
remove(ToRemove,Rest,LocRez,FinalRez),!.
remove(ToRemove,ListWithSublists,LocRez,FinalRez) :- ListWithSublists=[Sublist|Rest],
not(memberList(ToRemove,Sublist)),
append(LocRez,[Sublist],LocRezNew),
remove(ToRemove,Rest,LocRezNew,FinalRez),!.
> removeSupersetsList(List,Rez) :- removeSupersetsList(List,[],Rez). % call this for testing
%removeSupersetsList(List,LocRez,Final)
%remove the Head from List from the List itself if needed(obtain Rez in the process)
%append the Head into our LocRez(get LocRezNew),
%call this recursively for the Rez
removeSupersetsList(List,LocRez,LocRez) :- List=[] ,!.
removeSupersetsList(List,LocRez,Final) :- ( List=[ToRemove|_] ; List=[ToRemove] ),
remove(ToRemove,List,[],Rez),
append(LocRez,[ToRemove],LocRezNew),
removeSupersetsList(Rez,LocRezNew,Final),!.