Reorder list in prolog - prolog

I want to rearrange a list according to the occurrence of length of sublists from small to large.For example,the expected answer is rearranged by the number of the length of sub-list,there is one length of 4,two length of 1, three length of 3 and four length of 2.
Example query and its result:
lists_ascending([[a,b],[c],[a,b,c],[d,d,d],[d,s],[s],[d,s,s,a], [s,a],[s,t],[a,b,w]],Ls).
Ls = [[d,s,s,a],[c],[s],[a,b,c],[d,d,d],[a,b,w],[a,b],[d,s],[s,a],[s,t]]
My idea is that to calculate each length first and then do rearrangement. What I have done so far is to collect the number of sublist whose length is equal to the first sublist using the pattern [length-number].
count([],[0-0]).
count([A|B],[L-N]):-
length(A,L),
same_length(B,L,M),
N is M+1.
same_length([],_,0).
same_length([A|B],L,N) :-
( length(A,L)->
same_length(B,L,M),
N=M+1
; same_length(B,L,N)
).
The count(LIST,X) output is as followed:
21 ?- count_slot([[2],[3],[4],[2,3,4]],X).
X = [1-3].
But the expected output is [1-3,3-1], I don't know how to deal with the rest sublist(remove one by one??) and rearrange them according to the pattern [1-3,3-1].
Can somebody help? Thanks in advanced.

You need to account for the case that there may be multiple sublists of the same length as the first sublist. Clearly, your count/2 only ever describes the case where there is at most a single one of them, because its second argument is a list of length 1.
To gather the sublists of the same length as the first sublist, consider for example:
lists_same_length_as_first([Ls|Lss], Subs) :-
length(Ls, L),
phrase(length_sublists(L,Lss), Subs).
length_sublists(_, []) --> [].
length_sublists(L, [Sub|Subs]) -->
( { length(Sub, L) } -> [Sub]
; []
),
length_sublists(L, Subs).
which you can also write more compactly by using include/3:
lists_same_length_as_first([Ls|Lss], Subs) :-
length(Ls, L),
include(length_equal(L), Lss, Subs).
length_equal(L, Ls) :- length(Ls, L).
Example query and its result:
?- lists_same_length_as_first([[2],[3],[4],[2,3,4]], Ls).
Ls = [[3], [4]].
By the way, I gave a complete solution to this problem here.

Related

Find length of a list of lists

I wrote a predicate which should calculate the length of a list:
my_length([],0).
my_length([_|L],N) :- my_length(L,N1), N is N1 + 1.
Can anyone help in adjusting this so that it will take a list of lists and output the total number of elements in the list of lists?
You have most of what you need: add a rule that computes the length of a list of lists that passes the head on to my_length:
my_length_lol([], 0).
my_length_lol([H|L],N) :- my_length(H,Add), my_length_lol(L,N1), N is N1 + Add.
As you can see, my_length_lol ("lol" stands for "List of Lists") is a near exact copy of my_length. The only difference is that it does not ignore list head, and uses my_length rule to compute the length of a sublist.
Demo.
Both the solution posted #dasblinkenlight and the original code in the question can be made tail-recursive by using accumulators, which would allow running in constant space:
my_length(List, Length) :-
my_length(List, 0, Length).
my_length([], Length, Length).
my_length([_| Tail], Length0, Length) :-
Length1 is Length0 + 1,
my_length(Tail, Length1, Length).
my_length_lol(Lists, TotalLength) :-
my_length_lol(Lists, 0, TotalLength).
my_length_lol([List| Lists], TotalLength0, TotalLength) :-
my_length(List, Length),
TotalLength1 is TotalLength0 + Length,
my_length_lol(Lists, TotalLength1, TotalLength).
I am still not the biggest fan of foldl/4 and thus I find it much more natural to state:
xss_length(Xss, N) :-
maplist(length,Xss, Ns),
list_sum(Ns, N).
Still, this does not terminate for Xss = [_,_], xss_length(Xss, 2). But it's a start.
In this answer we use meta-predicate foldl/4 in combination with Prolog lambda expressions.
:- use_module(library(lambda)).
We define the predicate lists_length/2 like this:
lists_length(Xss,N) :-
foldl(\Xs^N0^N2^(length(Xs,N1),N2 is N0+N1), Xss, 0,N).
Sample query:
?- lists_length([[a,b,c],[],[d,e]], N).
N = 5.

Prolog: Rotate list n times right

Working on a predicate, rotate(L,M,N), where L is a new list formed by rotating M to the right N times.
My approach was to just append the tail of M to its head N times.
rotate(L, M, N) :-
( N > 0,
rotate2(L, M, N)
; L = M
).
rotate2(L, [H|T], Ct) :-
append(T, [H], L),
Ct2 is Ct - 1,
rotate2(L, T, Ct2).
Currently, my code returns L equal to the original M, no matter what N is set to.
Seems like when I'm recursing, the tail isn't properly moved to the head.
You can use append to split lists, and length to create lists:
% rotate(+List, +N, -RotatedList)
% True when RotatedList is List rotated N positions to the right
rotate(List, N, RotatedList) :-
length(Back, N), % create a list of variables of length N
append(Front, Back, List), % split L
append(Back, Front, RotatedList).
Note: this only works for N <= length(L). You can use arithmetic to fix that.
Edit for clarity
This predicate is defined for List and N arguments that are not variables when the predicate is called. I inadvertently reordered the arguments from your original question, because in Prolog, the convention is that strictly input arguments should come before output arguments. So, List and N and input arguments, RotatedList is an output argument. So these are correct queries:
?- rotate([a,b,c], 2, R).
?- rotate([a,b,c], 1, [c,a,b]).
but this:
?- rotate(L, 2, [a,b,c]).
will go into infinite recursion after finding one answer.
When reading the SWI-Prolog documentation, look out for predicate arguments marked with a "?", as in length. They can be used as shown in this example.

Prolog - sequence in list

We want to build a predicate that gets a list L and a number N and is true if N is the length of the longest sequence of list L.
For example:
?- ls([1,2,2,4,4,4,2,3,2],3).
true.
?- ls([1,2,3,2,3,2,1,7,8],3).
false.
For this I built -
head([X|S],X). % head of the list
ls([H|T],N) :- head(T,X),H=X, NN is N-1 , ls(T,NN) . % if the head equal to his following
ls(_,0) :- !. % get seq in length N
ls([H|T],N) :- head(T,X) , not(H=X) ,ls(T,N). % if the head doesn't equal to his following
The concept is simply - check if the head equal to his following , if so , continue with the tail and decrement the N .
I checked my code and it works well (ignore cases which N = 1) -
ls([1,2,2,4,4,4,2,3,2],3).
true ;
false .
But the true answer isn't finite and there is more answer after that , how could I make it to return finite answer ?
Prolog-wise, you have a few problems. One is that your predicate only works when both arguments are instantiated, which is disappointing to Prolog. Another is your style—head/2 doesn't really add anything over [H|T]. I also think this algorithm is fundamentally flawed. I don't think you can be sure that no sequence of longer length exists in the tail of the list without retaining an unchanged copy of the guessed length. In other words, the second thing #Zakum points out, I don't think there will be a simple solution for it.
This is how I would have approached the problem. First a helper predicate for getting the maximum of two values:
max(X, Y, X) :- X >= Y.
max(X, Y, Y) :- Y > X.
Now most of the work sequence_length/2 does is delegated to a loop, except for the base case of the empty list:
sequence_length([], 0).
sequence_length([X|Xs], Length) :-
once(sequence_length_loop(X, Xs, 1, Length)).
The call to once/1 ensures we only get one answer. This will prevent the predicate from usefully generating lists with sequences while also making the predicate deterministic, which is something you desired. (It has the same effect as a nicely placed cut).
Loop's base case: copy the accumulator to the output parameter:
sequence_length_loop(_, [], Length, Length).
Inductive case #1: we have another copy of the same value. Increment the accumulator and recur.
sequence_length_loop(X, [X|Xs], Acc, Length) :-
succ(Acc, Acc1),
sequence_length_loop(X, Xs, Acc1, Length).
Inductive case #2: we have a different value. Calculate the sequence length of the remainder of the list; if it is larger than our accumulator, use that; otherwise, use the accumulator.
sequence_length_loop(X, [Y|Xs], Acc, Length) :-
X \= Y,
sequence_length([Y|Xs], LengthRemaining),
max(Acc, LengthRemaining, Length).
This is how I would approach this problem. I don't know if it will be useful for you or not, but I hope you can glean something from it.
How about adding a break to the last rule?
head([X|S],X). % head of the list
ls([H|T],N) :- head(T,X),H=X, NN is N-1 , ls(T,NN) . % if the head equal to his following
ls(_,0) :- !. % get seq in length N
ls([H|T],N) :- head(T,X) , not(H=X) ,ls(T,N),!. % if the head doesn't equal to his following
Works for me, though I'm no Prolog expert.
//EDIT: btw. try
14 ?- ls([1,2,2,4,4,4,2,3,2],2).
true ;
false.
Looks false to me, there is no check whether N is the longest sequence. Or did I get the requirements wrong?
Your code is checking if there is in list at least a sequence of elements of specified length. You need more arguments to keep the state of the search while visiting the list:
ls([E|Es], L) :- ls(E, 1, Es, L).
ls(X, N, [Y|Ys], L) :-
( X = Y
-> M is N+1,
ls(X, M, Ys, L)
; ls(Y, 1, Ys, M),
( M > N -> L = M ; L = N )
).
ls(_, N, [], N).

Prolog Question - How to generate sublists of a given length

I want to generate all the sublists of a given list with the given property that they have a certain length mentioned as argument and also they have as a containing element a given element which is passed as a parameter. I have managed to do this but with the help of two predicates, and in terms of optimality is very slow:
sublist([], []).
sublist([A|T], [A|L]):-
sublist(T, L).
sublist(T, [_|L]):-
sublist(T, L).
choose(T, L):-
sublist(T, L),
(dimension(2, T); dimension(1, T)),
belongs(f, T).
In here I would like to return through the T parameter of the choose predicate all the sublists of the L list which have the dimension 2 or 1 and which contains the f element. The predicates dimension and member has the same usage as the predefined predicates length, respectively member.Can you please tell me how to incorporate this two conditions within the sublist predicate so that the program builds only those particular sublists?
The following builds subsequences of length MinLen =< Len =< MaxLen. I've no idea why you renamed length and member, so I'm going to use the originals. sublist/4 calls your sublist/2.
sublist(Sub,List,MinLen,MaxLen) :-
between(MinLen,MaxLen,Len),
length(Sub,Len),
sublist(Sub,List).
Note that length is called on two variables, so you get an iterative deepening search. choose/2 can now be defined as
choose(Sub,List) :-
sublist(Sub,List,1,2),
member(f,Sub).
This is the clean solution. If it's is not fast enough, then roll all the conditions into one predicate:
choose(Sub,List),
(Sub = [f] ; Sub = [f,_] ; Sub = [_,f]),
sublist(Sub,List).

create a list from a list of lists

I need to do the following: given a list of lists I need to find all possible combinations of the lists such that if some of these lists belong in such a combination, then they have no elements in common and the list created by appending the lists in the combination has a given length. Any ideas?
Example:
Say P= [[1,2,3],[4,5,6],[2,5],[7,9],[7,10],[8],[10]].
N a given number, say N=10. I need to search through P in order to find appropriate lists, with no elements in common, and add them in a list L such that the length of the union of L is 10. So in the above example :
L=[[1,2,3],[4,5,6],[7,9],[8],[10]]. It might be very easy but I'm new in Prolog
Given nobody's answered, and it's been quite a while since I've written anything in Prolog and I figured I needed the practice, here's how you'd do it.
First, to make generating the combinations easier, we create a term to preprocess the lists to pair them with their lengths to avoid having to get the lengths multiple times. The cut avoids needless backtracking:
with_lengths([], []) :- !.
with_lengths([H|T1], [(Len, H)|T2]) :-
length(H, Len),
with_lengths(T1, T2).
Here's the comb/3 predicate, which you use for generating the combinations:
comb(L, R, Max) :-
with_lengths(L, L1),
comb1(L1, R, Max).
comb1/3 does the actual work. The comments explain what's going on:
% Combination works.
comb1([], [], 0).
% Try combining the current element with the remainder.
comb1([(Len, Elem)|T1], [Elem|T2], Max) :-
NewMax is Max - Len,
comb1(T1, T2, NewMax).
% Alternatively, ignore the current element and try
% combinations with the remainder.
comb1([_|T1], T2, Max) :-
comb1(T1, T2, Max).

Resources