Removing Duplicates while maintaining order in Prolog - list

I am trying to create a predicate that removes duplicates from a list while maintaining its relative order. For example a list that is [1,2,2,3,4,5,5,2] should return [1,2,3,4,5]. However, my code is only able to remove consecutive duplicates and for instance, not the 2 at the end.
remove_duplicates([],[]).
remove_duplicates([H],[H]).
remove_duplicates([H,H|T],[H|List]) :- remove_duplicates(T,List).
remove_duplicates([H,X|T],[H|T1]):- X\=H, remove_duplicates([X|T],T1).
Another approach I was thinking of was to use member to see if the Tail contains the Head. However, the only way I can think of solving it that way is where I would remove the head, if head is a member of tail. This would however keep the last instance of the number only and break the relative order of the numbers in the list.
For instance:
[1,2,2,3,4,5,5,2]
[1,3,4,5,2]
When I actually want
[1,2,3,4,5]

You can make use of an accumulator: an extra parameter, here a list that is initially empty and when new elements arise will grow. Each recursive call the list is passed (or an updated copy).
For example:
remove_duplicates(LA, LB) :-
remove_duplicates(LA, LB, []).
remove_duplicates([], [], _).
remove_duplicates([H|T], R, Seen) :-
( member(H, Seen)
-> (R = S, Seen1 = Seen)
; (R = [H|S], Seen1 = [H|Seen])
),
remove_duplicates(T, S, Seen1).
This then gives us:
?- remove_duplicates([1,2,2,3,4,5,5,2], L).
L = [1, 2, 3, 4, 5].
Of course you can make use of more effective datastructures than a list. I leave that as an exercise.

Related

Implementation of predicate similar to Transpose in Prolog

I am pretty new to Prolog, actually 4 days into it and I came across an exercise that states :
Given a list of N lists with N size each implement a predicate called reshape(X,Y)
so that it :
Collects the first elements of all lists into a list.
Collects the second elements of all lists lists into a list.
...
Collects the N elements of all lists into a list.
Collects all lists mentioned above into a new list.
An example would be :
reshape([[1,2,3],[4,5,6],[7,8,9]],X)
X = [[1,4,7],[2,5,8],[3,6,9]]
So here is my implementation :
% Insert at the end of a list
insert([],X,[X]).
insert([H|T1],X,[H|T2]) :- insert(T1,X,T2).
% Length of list
len([],L,L).
len([_|T],L,X) :-
L1 is L + 1,
len(T,L1,X).
len(L,X) :- len(L,0,X).
% Create a list of N empty lists
init_list(L,0,L) :- !.
init_list(L,N,X) :-
N1 is N-1,
insert(L,[],Y),
init_list(Y,N1,X).
init_list(N,X) :- init_list([],N,X).
% Assign each element of a list to the corresponding list.
assign([],[],[]).
assign([H1|T1],[H2|T2],[Y|T3]) :-
insert(H2,H1,Y),
assign(T1,T2,T3).
% Reshape :
reshape([],L,L).
reshape([H1|T1],X,Result):-
assign(H1,X,Y),
reshape(T1,Y,Result).
reshape(Input,Result) :-
len(Input,N),
init_list(N,X),
reshape(Input,X,Result).
So the basic idea is that I start by creating a list of N empty lists and then for each list say L of the input I assign/add each element of L to the corresponding list.
Now I would appreciate some input as I already said I am new to Prolog and can't even tell what the time complexity of my predicate is.The only thing I know for a fact is that it works.
Howerever is there a better way I can implement it?
What's the time complexity of my implementation ? It seems like polynomial time but I can't really tell.
Thanks in advance.
You can code an O(N) algorithm that just goes through each element once:
reshape([[]|Tail], []):-
maplist(=([]), Tail).
reshape(Input, [Result|RTail]):-
reshape(Input, LTail, Result),
reshape(LTail, RTail).
reshape([], [], []).
reshape([[Item|Tail]|LTail], [Tail|MTail], [Item|RTail]):-
reshape(LTail, MTail, RTail).
reshape/3 gets the list with every first element of the list of lists. Then reshape/2 builds all such lists recursively.
Test case:
?- reshape([[1,2,3],[4,5,6],[7,8,9]],X).
X = [[1, 4, 7], [2, 5, 8], [3, 6, 9]] ;
false.

Change list of variables according to another list containing the index and atoms in prolog

I have a list of variables E and a list L and I want a predicate that works like this:
E=[A,B,C,D]
L=[(1,b),(3,m)]
solve(E,L).
E=[b,B,m,D]
Basically solve() should run through the list L and change E by using (a,b) to unify the variable at index a with the atom B. Is there any way to do this?
The meaning of the (badly named) solve/2 predicate is something like "for every pair (Index, Element), the Index-th element of the input list is Element". You are likely using a Prolog implementation that already has a predicate called something like nth1/3 which expresses "the Index-th element of List is Element". For example, in SWI-Prolog:
?- List = [A, B, C, D], nth1(3, List, this_is_the_third_element).
List = [A, B, this_is_the_third_element, D],
C = this_is_the_third_element.
So an alternative implementation of your predicate simply calls nth1/3 for each of your (Index, Element) pairs:
solve(_List, []).
solve(List, [(Index, Elem) | Pairs]) :-
nth1(Index, List, Elem),
solve(List, Pairs).
And with this you're done:
?- E = [A, B, C, D], L = [(1, b), (3, m)], solve(E, L).
E = [b, B, m, D],
A = b,
C = m,
L = [(1, b), (3, m)] ;
false.
Note that this solution is simple, but it has quadratic complexity in the length of the input list: nth1/3 might have to visit the entire N-element list N times. In the unlikely case that you need this predicate for a performance-critical part of some larger program, consider the more optimized solution sketched in the other answer.
Is there any way to do this?
Certainly. And as they say in Perl: "There is more than one way to do it".
Couple of problems:
Do not use (1,b). Use the idiomatic -(1,b) instead, which is written as 1-b (the pair). This gives you a list of pairs: L=[1-b,3-m]. There is a library specifically dealing with such pairs: https://www.swi-prolog.org/pldoc/man?section=pairs - alternatively you can use real maps implemented with AVL trees: https://www.swi-prolog.org/pldoc/man?section=assoc
Now you just need to:
sort the list of pairs, probably using keysort: https://www.swi-prolog.org/pldoc/doc_for?object=sort/2 or https://www.swi-prolog.org/pldoc/doc_for?object=sort/4
Go through the list left to right, keeping the current index, and performing a replacement when the next key in your sorted list is hit, or just retaining the existing term from the list otherwise. The result goes into an accumulator variable as head of a list.
Done! Special handling of out-of-bounds indexes etc. to be suitably handled by throwing or failing.
How to go through the sorted list of pairs (I didn not test this!):
% case of Index hit:
go_through([Index-Value|Rest],Index,InList,OutList) :-
InList = [I|Rest],
OutList = [Value|More],
succ(Index,NextIndex),
go_through(Rest,NextIndex,Rest,More).
% case of Index miss:
go_through([NotYetIndex-Value|Rest],Index,InList,OutList) :-
NotYetIndex > Index, % that should be the case
InList = [I|Rest],
OutList = [I|More],
succ(Index,NextIndex),
go_through(Rest,NextIndex,Rest,More).
go_through([],_,L,L). % DONE
Alternatively, you can write a replace0 that replaces-by-index in a list, and go through the L list.
Addendum: Working code using go_through
Actually contains a few subtlties
another_vectorial_replace1(ListIn,ReplacePairs,ListOut) :-
maplist([_,_]>>true,ListIn,ListOut), % Bonus code: This "makes sure" (i.e. fails if not)
% that ListIn and ListOut are the same length
maplist([(A,B),A-B]>>true,ReplacePairs,RealPairs), % Transform all (1,b) into [1,b]
maplist([K-_]>>integer(K),RealPairs), % Make sure the RealPairs all have integers on first place
keysort(RealPairs,RealPairsSorted), % Sorting by key, which are integers; dups are not removed!
debug(topic,"ListIn: ~q",[ListIn]),
debug(topic,"RealPairsSorted: ~q",[RealPairsSorted]),
go_through(RealPairsSorted,1,ListIn,ListOut),
debug(topic,"ListOut: ~q",[ListOut]).
% Case of Index hit, CurIndex is found in the first "Replacement Pair"
go_through([CurIndex-Value|RestPairs],CurIndex,ListIn,ListOut) :-
!, % Commit to choice
ListIn = [_|Rest],
ListOut = [Value|More],
succ(CurIndex,NextIndex),
go_through(RestPairs,NextIndex,Rest,More).
% Case of Index miss:
go_through([NotYetIndex-V|RestPairs],CurIndex,ListIn,ListOut) :-
NotYetIndex > CurIndex, % that should be the case because of sorting; fail if not
!, % Commit to choice
ListIn = [X|Rest],
ListOut = [X|More],
succ(CurIndex,NextIndex),
go_through([NotYetIndex-V|RestPairs],NextIndex,Rest,More).
% Case of DONE with list traversal
% Only succeed if there are not more pairs left (i.e. no out-of-bound replacements)
go_through([],_CurIndex,L,L).
% ===
% Tests
% ===
:- begin_tests(another_vectorial_replace1).
test(empty) :- another_vectorial_replace1([],[],LO),
LO=[].
test(nop_op) :- another_vectorial_replace1([a,b,c,d],[],LO),
LO=[a,b,c,d].
test(one) :- another_vectorial_replace1([a],[(1,xxx)],LO),
LO=[xxx].
test(two) :- another_vectorial_replace1([a,b,c,d],[(4,y),(2,x)],LO),
LO=[a,x,c,y].
test(full) :- another_vectorial_replace1([a,b,c,d],[(1,e),(2,f),(3,g),(4,h)],LO),
LO=[e,f,g,h].
test(duplicate_replacement,[fail]) :- another_vectorial_replace1([a],[(1,x),(1,y)],_).
test(out_of_bounds_high,[fail]) :- another_vectorial_replace1([a],[(2,y)],_).
test(out_of_bounds_low,[fail]) :- another_vectorial_replace1([a],[(0,y)],_).
:- end_tests(another_vectorial_replace1).
rt :- debug(topic),run_tests(another_vectorial_replace1).
Addendum 2
Replacement using maplist/N, foldl/N and library(assoc)
Recursive calls disappear behind the curtain!
https://github.com/dtonhofer/prolog_notes/blob/master/code/vector_replace0.pl
(the following assumes that the indices in the pairs list will be sorted, in increasing order, as the example in the question indicates.)
What you said can be written as one conjunction
E=[A,B,C,D], L=[(1,a),(3,c)], solve(E,L), E=[a,B,c,D].
which you intend to be holding under the proper definition of solve/2 that you seek to find. But isn't it like saying
E=[A|E2], L=[(1,a)|L2],
E2=[B,C,D], L2=[(3,c)],
solve(E2,L2), E2=[B,c,D],
E=[a|E2].
? Although, something doesn't quite fit right, here. c in E2 appears in second position, not 3rd as indicated by its entry in L2.
But naturally, L2 must be indexed from 2, since it is a tail of L which is indexed from 1. So we must make this explicit:
E=[A,B,C,D], L=[(1,a),(3,c)], solve(E,L), E=[a,B,c,D]
==
E=[A,B,C,D], L=[(1,a),(3,c)], solve(E,1,L), E=[a,B,c,D] % starting index 1
==
E=[A|E2], L=[(1,a)|L2],
E2=[B,C,D], L2=[(3,c)],
solve(E2,2,L2), E2=[B,c,D], E=[a|E2]
must, and now can, hold. But where did a get from, in E? What we actually mean here is
E=[A|E2], L=[(1,a)|L2],
p( (1,a), 1, a), % index match
E2=[B,C,D], L2=[(3,c)],
solve(E2,2,L2), E2=[B,c,D], % starting index 2
E=[a|E2]
with p/3 defined as
p( (I,A), I, A).
And so it must also hold that
E2=[B|E3], L2=[(3,c)],
\+ p( (3,c), 2, c), % index mismatch
E3=[C,D], L3=L2,
solve(E3,3,L3), E3=[c,D], E2=[B|E3]
L2 is not traversed along at this step (L3=L2), since p( (3,c), 2, c) does not hold.
Do you see how the recursive definition of solve/3 reveals itself here? Could you finish it up?

Take out first elements from nested lists in Prolog

Problem: I need to transform this list: [[1,2],[3,4],[5,6]] to [1,3,5], by taking only first items from each sub-list in first list and creating new list with all of them. Language is SWI-Prolog.
My solution: To do this, I wrote this code:
getFirstItems([], Result).
getFirstItems([H|T], Result) :-
[H2|T2] = H,
append(Result,H2,Result2),
getFirstItems(T, Result2).
Issue: But this fails to infinite recursion when tail always equals to [[5,6]]
Question: how to solve this issue and solve this problem correctly?
You are complicating things too much. You need to reason with a declarative mindset, and thus implement what the relationships between the list of lists and the list of first elements are.
Here is a solution:
first_items([], []).
first_items([[H|_]|T], [H|T2]) :-
first_items(T, T2).
Indeed, the only two things we need to state to describe that relationship are:
If the list of lists is empty, then so is the list of first elements.
a first element H is in the list of first elements, followed by the first elements (T2) of the rest of the list of lists (T).
Example queries:
?- first_items([[1,2],[3,4],[5,6]], Z).
Z = [1, 3, 5].
?- first_items(L, [1,3,4]).
L = [[1|_22058], [3|_22070], [4|_22082]].
?- first_items(L, Z).
L = Z, Z = [] ;
L = [[_22048|_22050]],
Z = [_22048] ;
L = [[_22048|_22050], [_22066|_22068]],
Z = [_22048, _22066]
…

Prolog List Predicates

I need some help with three prolog predicates for checking and manipulating lists. I'm new to prolog and any help would be much appreciated.
The three predicates are:
double_up(+List1, -List2) is true when List2 has each element of List1 twice. The query double_up([a,b,c],X) should give X=[a,a,b,b,c,c]. The order of the elements in the output list does not matter.
pivot(+List1, +Pivot, -Smaller, -GreaterEq) is true when Smaller is the list of numbers in List1 smaller than Pivot, and GreaterEq is the list of numbers in List1 bigger than or equal to Pivot.
fancy_replace(+List, +Takeout,+Putin, -NewList, -Count) is true when NewList is the same list as the input List, but where each Takeout element in the list is replaced with the Putin element. Count should be the number of Takeouts that got replaced. For example, the query fancy_replace([9,10,1,9,2],9,0, X, C) should give X = [0,10,1,0,2] and C = 2. The order of the elements in the output list does not matter.
The simpler pattern to process lists in Prolog imposes a recursive predicate with 2 arguments, matching - conventionally - input and output data, and a base case, stopping the recursion, matching the empty list. Then
double_up([X|Xs], [X,X|Ys]) :- double_up(Xs, Ys).
double_up([], []).
This predicate it's a bit more general than what's required, because it works also in mode double_up(-List1, +List2). For instance
?- double_up(L,[1,1,2,2]).
L = [1, 2].
To restrict its mode as required, I think it's necessary to uselessly complicate the code, moving that clean loop in a service predicate, and leaving double_up just to test the arguments:
double_up(I, O) :- is_list(I), var(O), double_up_(I, O).
double_up_([X|Xs], [X,X|Ys]) :- double_up_(Xs, Ys).
double_up_([], []).
pivot/4 could be 'one-liner' in SWI-Prolog:
pivot(List1, Pivot, Smaller, GreaterEq) :-
partition(>(Pivot), List1, Smaller, GreaterEq).
like partition, foldl from library(apply) it's an easy inplementation of the last required predicate:
fancy_replace(List, Takeout, Putin, NewList, Count) :-
foldl(swap_n_count(Takeout, Putin), List, NewList, 0, Count).
swap_n_count(Takeout, Putin, L, N, C0, C) :-
( L == Takeout
-> N = Putin, C is C0 + 1
; N = L, C = C0
).
to be honest, i hate prolog... even though it is fun and easy after you learn it
i think this is a good reference as I was having trouble understanding how prolog works couple weeks ago.
what does the follow prolog codes do?
anyway.. this is the answer for your first problem; Hopefully you could solve the rest yourself :D
double([]).
double([H|[]], [H,H|[]]).
double([H|T],[H,H|T1]):- double(T, T1).
btw, this might not the only solution...but it works

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),!.