Related
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.
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.
I want to find the bigger value from a list's element's pairs.
Ie. list=[5,7,4,5,6,8] the pairs are [5,7], [7,4], [4,5] etc.
Right now I have this little code snippet:
bigger([],X).
bigger([E1],[H|E1]).
bigger([E1,E2|T],[H|_]):-
(E1>E2,bigger([E2|T],[H|E1]));
(E1<E2,bigger([E2|T],[H|E2])).
The solution should look like:
?- bigger([5,7,4,5,6,8],X).
X = [7,7,5,6,8,8]
EDIT:
Deleted the remove/3 lines, since they're wrong.
I'll give MY understanding of how the code works.
Empty given list check.
One element list check, adds it to output list end ([H|E1])
More than one element in given list, output list
3.1 First two element check (E1 >/< E2)
3.2 New recursive query without E1 (first element)
3.3 Whichever is bigger is output list's last element now.
First I'll show you my solution of your problem (and the result shouldn't be X = [7,7,5,6,8]? I'll make this version.)
gtr(X,Y,Y) :-
Y>=X.
gtr(X,_,X).
bigger([],[]).
bigger([_], []).
bigger([X,Y|R], [Z|H]) :-
bigger([Y|R],H), gtr(X,Y,Z),!.
If you want to have last element appear in this list anyway than just change second bigger function.
Since the relation is describing lists you could opt to use DCGs for the task:
max_of(X,X,Y) :- X >= Y. % X is maximum if X>=Y
max_of(Y,X,Y) :- Y > X. % Y is maximum if Y>X
list_biggers(L,B) :-
phrase(biggers(L),B). % the DCG biggers//1 describes B based on L
biggers([]) --> % if the list is empty
[]. % there's no element in the biggers list
biggers([X]) --> % if the list contains just one element
[X]. % it is in the biggers list
biggers([X,Y|Xs]) --> % if the list contains at least two elements
{max_of(M,X,Y)}, % the maximum of them
[M], % is in the biggers list
biggers([Y|Xs]). % the same holds for [Y|Xs]
This definition is sticking to your reading of the task, that is, in the case of a one-element list the only element is in the list of bigger elements:
?- list_biggers([5,7,4,5,6,8],B).
B = [7, 7, 5, 6, 8, 8] ;
false.
?- list_biggers([1],B).
B = [1] ;
false.
If you prefer the reading suggested by #Armatorix, just change the second DCG-rule to
biggers([_X]) -->
[].
This way the queries above yields the following results:
?- list_biggers([5,7,4,5,6,8],B).
B = [7, 7, 5, 6, 8] ;
false.
?- list_biggers([1],B).
B = [] ;
false.
Note that the list has to be sufficiently instantiated. Otherwise you get an error:
?- list_biggers([X,Y,Z],B).
ERROR: >=/2: Arguments are not sufficiently instantiated
If the list only contains integers, you can remedy this problem by using CLP(FD). Add a line to include the library and change max_of/2 like so:
:- use_module(library(clpfd)).
max_of(X,X,Y) :- X #>= Y.
max_of(Y,X,Y) :- Y #> X.
Now the query above delivers all 4 expected solutions:
?- list_biggers([X,Y,Z],B).
B = [X, Y, Z],
X#>=Y,
Y#>=Z ;
B = [X, Z, Z],
X#>=Y,
Y#=<Z+ -1 ;
B = [Y, Y, Z],
X#=<Y+ -1,
Y#>=Z ;
B = [Y, Z, Z],
X#=<Y+ -1,
Y#=<Z+ -1 ;
false.
In order to construct logical programs, one needs to think logical. Based on the problem statement, there are three possibilities here:
we have an empty list, in that case the result is an empty list as well:
bigger([],[]).
in case we have a list with one element, the problem is underspecified. I would say that the result should be an empty list, but your example seems to suggest that we return that number, since we then have a 1-tuple, and the maximum of a 1-tuple is of course the single element in the tuple:
bigger([H],[H]).
in case the list contains two or more elements [H1,H2|T], then H1 and H2 are the first two elements. In that case we construct a vitual tuple in our head [H1,H2] and calculate the maximum, which is thus M is max(H1,H2). We prepend M to the resulting list of the recursion. That recursion is done on the list [H2|T]: the list where we popped H1 from:
bigger([H1,H2|T],[M|U]) :-
M is max(H1,H2),
bigger([H2|T],U).
Or putting this all together:
bigger([],[]).
bigger([H],[H]).
bigger([H1,H2|T],[M|U]) :-
M is max(H1,H2),
bigger(T,U).
I have code in PROLOG:
vals(_,[],_).
vals([H|T],[(H,C)|L],K) :- vals([H|T],L,(K,C)).
This code receivers list and list of tuples, for example:
vals([1],[(1,4),(1,2)],X).
I check if element from first list is equal to some tuples first element from the other list. In this case foundValues will return true, because 1 is equal to each tuples first element. This works fine, but instead of returning true/false, in resulting list I want to return all second element of each tuple where its first element is equal to the element from list. In this case X should be [4,2]. I am trying to do this with (K,C), but no success.
So, the question is - How to return list?
Here's an example on how to append to the list, just for 1 element.
Three cases:
For an empty list
When the element matches the first entry of the tupple
When the element not matches
Starting from this you should be able to create your example.
vals(_,[],[]).
vals(H,[(H,C)|L],[C|K]) :- vals(H,L,K).
vals(H,[(H2,_)|L],K) :-
H \= H2,
vals(H,L,K).
Example:
vals(1,[(1,2),(1,3)],X).
X = [2, 3] ;
false.
Extra:
Consider placing a cut in case 2.
Consider rewrite this with an accumulator
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),!.