Related
I am doing somthing horrible but I don't know how to make it better.
I am forming all pairwise sums of the elements of a List called SomeList, but I don't want to see duplicates ( I guess I want "all possible pairwise sums" ):
sets:to_list(sets:from_list([A+B || A <- SomeList, B <- SomeList]))
SomeList does NOT contain duplicates.
This works, but is horribly inefficient, because the original list before the set conversion is GIGANTIC.
Is there a better way to do this?
You could simply use lists:usort/1
lists:usort([X+Y || X <- L, Y <- L]).
if the chance to have duplicates is very high, then you can generate the sum using 2 loops and store the sum in an ets set (or using map, I didn't check the performance of both).
7> Inloop = fun Inloop(_,[],_) -> ok; Inloop(Store,[H|T],X) -> ets:insert(Store,{X+H}), Inloop(Store,T,X) end.
#Fun<erl_eval.42.54118792>
8> Outloop = fun Outloop(Store,[],_) -> ok; Outloop(Store,[H|T],List) -> Inloop(Store,List,H), Outloop(Store,T,List) end.
#Fun<erl_eval.42.54118792>
9> Makesum = fun(L) -> S = ets:new(temp,[set]), Outloop(S,L,L), R =ets:foldl(fun({X},Acc) -> [X|Acc] end,[],S), ets:delete(S), R end.
#Fun<erl_eval.6.54118792>
10> Makesum(lists:seq(1,10)).
[15,13,8,11,20,14,16,12,7,3,10,9,19,18,4,17,6,2,5]
11> lists:sort(Makesum(lists:seq(1,10))).
[2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
12>
This module will allow you to compare times of execution when using list comprehension, sets or ets. You can of course add additional functions to this comparison:
-module(pairwise).
-export([start/2]).
start(Type, X) ->
L = lists:seq(1, X),
timer:tc(fun do/2, [Type, L]).
do(compr, L) ->
sets:to_list(sets:from_list([A+B || A <- L, B <- L]));
do(set, L) ->
F = fun(Sum, Set) -> sets:add_element(Sum, Set) end,
R = fun(Set) -> sets:to_list(Set) end,
do(L, L, sets:new(), {F, R});
do(ets, L) ->
F = fun(Sum, Tab) -> ets:insert(Tab, {Sum}), Tab end,
R = fun(Tab) ->
Fun = fun({X}, Acc) -> [X|Acc] end,
Res = ets:foldl(Fun, [], Tab),
ets:delete(Tab),
Res
end,
do(L, L, ets:new(?MODULE, []), {F, R}).
do([A|AT], [B|BT], S, {F, _} = Funs) -> do([A|AT], BT, F(A+B, S), Funs);
do([_AT], [], S, {_, R}) -> R(S);
do([_A|AT], [], S, Funs) -> do(AT, AT, S, Funs).
Results:
36> {_, Res1} = pairwise:start(compr, 20).
{282,
[16,32,3,19,35,6,22,38,9,25,12,28,15,31,2,18,34,5,21,37,8,
24,40,11,27,14,30|...]}
37> {_, Res2} = pairwise:start(set, 20).
{155,
[16,32,3,19,35,6,22,38,9,25,12,28,15,31,2,18,34,5,21,37,8,
24,40,11,27,14,30|...]}
38> {_, Res3} = pairwise:start(ets, 20).
{96,
[15,25,13,8,21,24,40,11,26,20,14,28,23,16,12,39,34,36,7,32,
35,3,33,10,9,19,18|...]}
39> R1=lists:usort(Res1), R2=lists:usort(Res2), R3=lists:usort(Res3).
[2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,
24,25,26,27,28,29,30|...]
40> R1 = R2 = R3.
[2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,
24,25,26,27,28,29,30|...]
The last line is to compare that all functions return the same result but sorted differently.
First number in each resulted tuple is the time of execution as returned from timer:tc(fun do/2, [Type, L]).. In this example it's 282 for list comprehension, 155 for sets and 96 for ets.
An effective way is to use foldl instead of lists comprehension, because in this case you nedd a state on each step
sets:to_list(
lists:foldl(fun(A, S1) ->
lists:foldl(fun(B, S2) ->
sets:add_element(A+B, S2)
end, S1, SomeListA)
end, sets:new(), SomeListB)).
This solution keeps it relatively fast and makes use of as much pre-written library code as possible.
Note that I use lists:zip/2 here rather than numeric +, only to illustrate that this approach works for any kind of non-repeating permutation of a unique list. You may only care about arithmetic, but if you want more, this can do it.
-export([permute_unique/1]).
permute_unique([]) ->
[];
permute_unique([A|Ab]) ->
lists:zip(lists:duplicate(length(Ab)+1, A), [A|Ab])
++
permute_unique(Ab).
%to sum integers, replace the lists:zip... line with
% [B+C || {B,C} <- lists:zip(lists:duplicate(length(Ab)+1, A), [A|Ab])]
%to perform normal arithmetic and yield a numeric value for each element
I am not sure what you consider gigantic - you will end up with N*(N+1)/2 total elements in the permuted list for a unique list of N original elements, so this gets big really fast.
I did some basic performance testing of this, using an Intel (Haswell) Core i7 # 4GHz with 32GB of memory, running Erlang/OTP 17 64-bit.
5001 elements in the list took between 2 and 5 seconds according to timer:tc/1.
10001 elements in the list took between 15 and 17 seconds, and required about 9GB of memory. This generates a list of 50,015,001 elements.
15001 elements in the list took between 21 and 25 seconds, and required about 19GB of memory.
20001 elements in the list took 49 seconds in one run, and peaked at about 30GB of memory, with about 200 million elements in the result. That is the limit of what I can test.
What's the Erlang equivalent to the following Python code:
for x in range(9):
for y in range(9):
for z in range(9):
foo(x, y, z)
I know I can generate the product first with C = [{X,Y,Z} || X<- lists:seq(1,9), Y<- lists:seq(1,9), Z<- lists:seq(1,9)] then foo([])->done; foo([H|T])->blah blah.
How do I do it without an auxiliary list, using recursion only?
You could do it with three recursive functions.
You might be able to do it with some complex pattern-matching in function head.
But easiest way to skip creation of auxiliary list is to call your function inside list comprehension
C = [foo(X, Y, Z) || X<- lists:seq(1,9),
Y<- lists:seq(1,9),
Z<- lists:seq(1,9)]
Where foo/3 process one element.
List comprehension still forces you to create auxiliary lists in memory.
In case of dealing with huge data sets you should avoid it. Writing recursive functions every time is also awkward so i came up with my own generic for function. It's a little bit slower in traversing than direct recursion or list comprehension but it's memory stable, generic and easy to use.
Usage:
(for({10}))(
fun (X) -> io:format("~p ",[X]) end).
> 1 2 3 4 5 6 7 8 9 10
(for({10, -10, -2}))(
fun (X) -> io:format("~p ",[X]) end).
> 10 8 6 4 2 0 -2 -4 -6 -8 -10
Works with lists too:
(for(lists:seq(10, -10, -2)))(
fun (X) -> io:format("~p ",[X]) end).
> 10 8 6 4 2 0 -2 -4 -6 -8 -10
It's also possible to define step or guard as a function:
(for({256, 1.1, fun (X) -> math:sqrt(X) end, fun (X, Range) -> X > Range end}))(
fun (X) -> io:format("~p ",[X]) end).
> 256 16.0 4.0 2.0 1.4142135623730951 1.189207115002721
If you pass to for a two parameter function, then you can use accumulator feature just like with lists:foldl/3. You also need to pass initial accumulator to for:
Fact = (for(1, {1, 5}))(
fun(X, Acc) ->
X * Acc
end),
io:format("~p", [Fact]).
> 120
e_fact(N) ->
{_, E} = (for({1, 1}, {1, N}))( % i assumed 1/0! equals 1
fun(X, {LastFact, Sum}) ->
Fact = LastFact * X,
{Fact, Sum + 1 / Fact}
end),
E.
io:format("e=~p", [e_fact(10)]).
> e=2.7182818011463845
Also step and guard functions can be dependent on accumulator. Just pass function with one more parameter.
Nested loops finding Pythagorean triples. Easy with closures:
pyth_lists(N) ->
[io:format("~p ", [{A, B, C}]) ||
A <- lists:seq(1, N),
B <- lists:seq(A + 1, N),
C <- lists:seq(B + 1, N),
A * A + B * B == C * C].
pyth_for(N) ->
(for({1, N}))(
fun(A) ->
(for({A + 1, N}))(
fun(B) ->
(for({B + 1, N}))(
fun(C) ->
case A * A + B * B == C * C of
true -> io:format("~p ", [{A, B, C}]);
false -> ok
end
end)
end)
end).
It's too small for external repository. I keep it in my utilities module.
If you find it helpful, here is code:
-export([for/1, for/2]).
for(Through) ->
for([], Through).
for(InitAcc, Opts) when is_tuple(Opts) ->
{Init, Range, Step, Guard} = for_apply_default_opts(Opts),
fun(Fun) ->
UpdFun = if
is_function(Fun, 1) ->
fun(I, _FAcc) -> Fun(I) end;
is_function(Fun, 2) ->
Fun
end,
for_iter(UpdFun, InitAcc, Init, Range, Step, Guard) end;
for(InitAcc, List) when is_list(List) ->
fun(Fun) -> for_list_eval(Fun, InitAcc, List) end.
for_iter(Fun, Acc, I, Range, Step, Guard) ->
case Guard(I, Range, Acc) of
false ->
Acc;
true ->
NewAcc = Fun(I, Acc),
for_iter(Fun, NewAcc, Step(I, NewAcc), Range, Step, Guard)
end.
for_list_eval(Fun, Acc, List) ->
if
is_function(Fun, 1) ->
lists:foreach(Fun, List);
is_function(Fun, 2) ->
lists:foldl(Fun, Acc, List)
end.
for_apply_default_opts({Range}) ->
DefaultInit = 1,
for_apply_default_opts({DefaultInit, Range});
for_apply_default_opts({Init, Range}) ->
DefaultStep = 1,
for_apply_default_opts({Init, Range, DefaultStep});
for_apply_default_opts({Init, Range, Step}) ->
DefaultGuard = case (Step > 0) or is_function(Step) of
true -> fun(I, IterRange, _Acc) -> I =< IterRange end;
false -> fun(I, IterRange, _Acc) -> I >= IterRange end
end,
for_apply_default_opts({Init, Range, Step, DefaultGuard});
for_apply_default_opts({Init, Range, Step, Guard}) when is_function(Guard, 2) ->
for_apply_default_opts({Init, Range, Step, fun(I, IterRange, _Acc) -> Guard(I, IterRange) end});
for_apply_default_opts({Init, Range, Step, DefaultGuard}) when is_number(Step) ->
for_apply_default_opts({Init, Range, fun(I, _Acc) -> I + Step end, DefaultGuard});
for_apply_default_opts({Init, Range, Step, DefaultGuard}) when is_function(Step, 1) ->
for_apply_default_opts({Init, Range, fun(I, _Acc) -> Step(I) end, DefaultGuard});
for_apply_default_opts({_Init, _Range, _Step, _DefaultGuard} = Opts) ->
Opts.
Is there an equivalent expression for the increment/decrement operator e.g. counter++?
I also wonder how to properly do this?
-module(whileloop).
-export([call/0, while_loop/2]).
call() ->
while_loop(10,0).
while_loop(Var,Counter) ->
case Var =:= Counter of
false ->
Counter += 1,
whileloop(Var);
end.
edit:
-module(whileloop).
-export([call/0, while_loop/2]).
call() ->
while_loop(10,0).
while_loop(Var, Counter) ->
case Var =:= Counter of
false ->
while_loop(Var,Counter + 1)
end.
The meaning of C += 1 is to modify the value of C. It is a non sense in Erlang since it can only give the following result:
1> C = C+1.
* 1: variable 'C' is unbound
C = 1.
1
3> C = C+1.
** exception error: no match of right hand side value 2
Keep in mind that "A = B" does not mean assigns the value of B to A, but "pattern match" A against B,
if A is unbound then it will bind to A the value of B;
if A =:= B nothing is done, the process continue;
if A =/= B then the process crashes.
So yes, if you want to have a counter, or any information that change, you must use a state variable which is passed as argument of a recursive loop. From this point of view, your last code is correct, but lets follow what happens when you call "call()" in the shell.
first it calls, in the same process - the shell - the function while_loop(10,0).
10 is not equal to 0 so it calls immediately while_loop(10,1).
10 is not equal to 1 so it calls immediately while_loop(10,2).
and so on until it calls while_loop(10,10). Now 10 =:= 10 is true, and this result does not match any clause of the case, so you get an error and the process crash.
As your code does not contain any message receive and simply loop an loop until it crashes, thhe wole process takes only a few micro seconds, so it looks like it fails immediately.
Depending on what you expect, you can imagine several types of counter, here are 2 examples:
-module(counter).
-compile(export_all).
% one counter that help you to count some events
% interface
start_c1(End) when is_integer(End) ->
spawn(?MODULE,counter1,[End]).
start_link_c1(End) when is_integer(End) ->
spawn_link(?MODULE,counter1,[End]).
inc_c1(Pid) when is_pid(Pid) ->
Ref = make_ref(),
Pid ! {inc,self(),Ref},
receive
{Ref,done} -> done;
{Ref,V} -> V
after 1000 ->
{error,no_response}
end.
value_c1(Pid) when is_pid(Pid) ->
Ref = make_ref(),
Pid ! {get_value,self(),Ref},
receive
{Ref,V} -> V
after 1000 ->
{error,no_response}
end.
stop_c1(Pid) when is_pid(Pid) ->
Pid ! stop.
% the counter
counter1(End) -> counter1_loop(End,0).
counter1_loop(End,V) ->
receive
{inc,Pid,Ref} when V =/= done ->
NewV = case V+1 of
End -> done;
Nv -> Nv
end,
Pid ! {Ref,NewV},
counter1_loop(End,NewV);
{inc,Pid,Ref} ->
Pid ! {Ref,done},
counter1_loop(End,done);
{get_value,Pid,Ref} ->
Pid ! {Ref,V},
counter1_loop(End,V);
stop ->
ok
end.
% One kind of timeout that execute something after a while -
% note it exists a similar one in the library
start_after(T,M,F,A) when is_integer(T), is_list(A) ->
Ref = make_ref(),
{Ref,spawn(?MODULE,after_receive,[T,M,F,A,self(),Ref])}.
cancel_after(P) when is_pid(P) ->
P ! cancel.
% the counter
after_receive(T,M,F,A,Pid,Ref) ->
receive
{cancel,Ref} -> Pid ! {after_receive,Ref,cancelled}
after T ->
Pid ! {after_receive,Ref,done},
apply(M,F,A)
end.
and here how to use them:
1> c("../src/counter").
{ok,counter}
2> {Ref,P} = counter:start_after(5000,io,format,["This is the end!" ]).
{#Ref<0.0.0.29>,<0.33.0>}
This is the end!3>
3> {Refa,Pa} = counter:start_after(50000,io,format,["This is the end!" ]).
{#Ref<0.0.0.34>,<0.35.0>}
4> Pa ! {cancel,Refa}.
{cancel,#Ref<0.0.0.34>}
5> flush().
Shell got {after_receive,#Ref<0.0.0.29>,done}
Shell got {after_receive,#Ref<0.0.0.34>,cancelled}
ok
6> P1 = counter:start_c1(5).
<0.52.0>
7> counter:inc_c1(P1).
1
8> counter:inc_c1(P).
{error,no_response}
9> counter:inc_c1(P1).
2
10> counter:inc_c1(P1).
3
11> counter:value_c1(P1).
3
12> counter:inc_c1(P1).
4
13> counter:inc_c1(P1).
done
14> counter:value_c1(P1).
done
15> counter:inc_c1(P1).
done
16> counter:stop_c1(P1).
stop
17> counter:inc_c1(P1).
{error,no_response}
18>
Just recursively call while_loop with the Counter argument incremented by one:
while_loop(Var, Counter + 1)
Your edited version doesn't have a clause when Var =:= Counter and thus crashes. And you'd better use pattern matching in function clauses.
-module(whileloop).
-export([call/0, while_loop/2]).
call() ->
while_loop(10,0).
while_loop(Var, Var) ->
ok;
while_loop(Var, Counter) ->
while_loop(Var, Counter + 1).
And of course you'd need to do something inside the loop. You can use lambdas for that:
-module(whileloop).
-export([call/0, while_loop/3]).
call() ->
while_loop(10, 0, fun(Counter) -> io:format("Counter: ~p~n", [Counter]) end).
while_loop(Var, Var, _) ->
ok;
while_loop(Var, Counter, Fun) ->
Fun(Counter),
while_loop(Var, Counter + 1, Fun).
I am new to Erlang, I need to spawn two process running add function, then add the
two numbers.
assigned value of Process one and two is showing the process id, I need catch the value.
How can I read the return value of add(N) function in my calc function?
-module(myerl).
-export([calc/1,add/1]).
add(N) ->
N + 5.
calc(L)
pone = spawn( fun() -> add(A) end),
ptwo = spawn( fun() -> add(B) end),
Result = Pone + Ptwo,
io:format("result ~p~n", [Result]).
You need to use message passing. You must send a message back to the calling process with the result. The spawn function returns a PID (process identifier) to the newly spawned process, not the result of its execution.
This example should do what you're expecting:
calc(A, B) ->
Self = self(), % The spawned funs need a Pid to send to, use a closure
POne = spawn(fun() -> Self ! {self(), add(A)} end),
PTwo = spawn(fun() -> Self ! {self(), add(B)} end),
wait_for_response(POne, PTwo, 0).
wait_for_response(undefined, undefined, Sum) ->
Sum;
wait_for_response(POne, PTwo, Sum) ->
receive
{POne, V} -> wait_for_response(undefined, PTwo, Sum + V);
{PTwo, V} -> wait_for_response(POne, undefined, Sum + V)
end.
#Soup d'Campbells' explanation is good. I instinctively did something slightly different which, in a toy way, anticipates some bad behavior with the child processes. Also, I allow the input to be a list of numbers, not just 2.
-module(myerl).
-export([calc/1, add/1]).
calc(NumList) when is_list(NumList)->
Parent = self(),
_Pids = [spawn(fun()-> Parent ! add(ANum) end) || ANum <- NumList],
collect(length(NumList), 0);
calc(_) ->
{error, badarg}.
collect(0, Sum) ->
Sum;
collect(Cnt, Sum) ->
receive
N when is_number(N) ->
collect(Cnt-1, Sum + N);
_Bad -> % returned something that isnt a number
collect(Cnt-1, Sum)
after 1000 -> % died or is too slow
collect(Cnt-1, Sum)
end.
add(N) ->
N + 5.
I want to split:
[1,2,3,4,5,6,7,8]
into:
[[1,2],[3,4],[5,6],[7,8]]
It generally works great with:
[ lists:sublist(List, X, 2) || X <- lists:seq(1,length(List),2) ] .
But it is really slow this way. 10000 Elements take amazing 2.5 seconds on my netbook. I have also written a really fast recursive function, but I am simply interested: Could this list comprehension also be written in a different way, so that it is faster?
Try this:
part(List) ->
part(List, []).
part([], Acc) ->
lists:reverse(Acc);
part([H], Acc) ->
lists:reverse([[H]|Acc]);
part([H1,H2|T], Acc) ->
part(T, [[H1,H2]|Acc]).
Test in erlang-shell (I've declared this function in module part):
2> part:part([1,2,3,4,5,6,7,8]).
[[1,2],[3,4],[5,6],[7,8]]
3>
3> timer:tc(part, part, [lists:seq(1,10000)]).
{774,
[[1,2],
[3,4],
[5,6],
[7,8],
"\t\n","\v\f",
[13,14],
[15,16],
[17,18],
[19,20],
[21,22],
[23,24],
[25,26],
[27,28],
[29,30],
[31,32],
"!\"","#$","%&","'(",")*","+,","-.","/0","12","34",
[...]|...]}
Just 774 microseconds (which is ~0,8 milliseconds)
Here are two quick solutions for you that are both flexible. One is easy to read, but only slightly faster than your proposed solution. The other is quite fast, but is a bit cryptic to read. And note that both of my proposed algorithms will work for lists of anything, not just numeric ordered lists.
Here is the "easy-to-read" one. Call by n_length_chunks(List,Chunksize). For example, to get a list of chunks 2 long, call n_length_chunks(List,2). This works for chunks of any size, ie, you could call n_length_chunks(List,4) to get [[1,2,3,4],[5,6,7,8],...]
n_length_chunks([],_) -> [];
n_length_chunks(List,Len) when Len > length(List) ->
[List];
n_length_chunks(List,Len) ->
{Head,Tail} = lists:split(Len,List),
[Head | n_length_chunks(Tail,Len)].
The much faster one is here, but is definitely harder to read, and is called in the same way: n_length_chunks_fast(List,2) (I've made one change to this compared with the one above, in that it pads the end of the list with undefined if the length of the list isn't cleanly divisible by the desired chunk length.
n_length_chunks_fast(List,Len) ->
LeaderLength = case length(List) rem Len of
0 -> 0;
N -> Len - N
end,
Leader = lists:duplicate(LeaderLength,undefined),
n_length_chunks_fast(Leader ++ lists:reverse(List),[],0,Len).
n_length_chunks_fast([],Acc,_,_) -> Acc;
n_length_chunks_fast([H|T],Acc,Pos,Max) when Pos==Max ->
n_length_chunks_fast(T,[[H] | Acc],1,Max);
n_length_chunks_fast([H|T],[HAcc | TAcc],Pos,Max) ->
n_length_chunks_fast(T,[[H | HAcc] | TAcc],Pos+1,Max);
n_length_chunks_fast([H|T],[],Pos,Max) ->
n_length_chunks_fast(T,[[H]],Pos+1,Max).
Tested on my (really old) laptop:
Your proposed solution took about 3 seconds.
My slow-but-readable one was slightly faster and takes about 1.5 seconds (still quite slow)
My fast version takes about 5 milliseconds.
For completeness, Isac's solution took about 180 milliseconds on my same machine.
Edit: wow, I need to read the complete question first. Oh well I'll keep here for posterity if it helps. As far as I can tell, there's not a good way to do this using list comprehensions. Your original version is slow because each iteration of sublist needs to traverse the list each time to get to each successive X, resulting in complexity just under O(N^2).
Or with a fold:
lists:foldr(fun(E, []) -> [[E]];
(E, [H|RAcc]) when length(H) < 2 -> [[E|H]|RAcc] ;
(E, [H|RAcc]) -> [[E],H|RAcc]
end, [], List).
I want to submit slightly complicated but more flexible (and mostly faster) solution of one proposed by #Tilman
split_list(List, Max) ->
element(1, lists:foldl(fun
(E, {[Buff|Acc], C}) when C < Max ->
{[[E|Buff]|Acc], C+1};
(E, {[Buff|Acc], _}) ->
{[[E],Buff|Acc], 1};
(E, {[], _}) ->
{[[E]], 1}
end, {[], 0}, List)).
so function part can be implemented as
part(List) ->
RevList = split_list(List, 2),
lists:foldl(fun(E, Acc) ->
[lists:reverse(E)|Acc]
end, [], RevList).
update
I've added reverse in case if you want to preserve order, but as I can see it adds no more than 20% of processing time.
You could do it like this:
1> {List1, List2} = lists:partition(fun(X) -> (X rem 2) == 1 end, List).
{[1,3,5|...],[2,4,6|...]}
2> lists:zipwith(fun(X, Y) -> [X, Y] end, List1, List2).
[[1,2],[3,4],[5,6]|...]
This takes ~73 milliseconds with a 10000 elements List on my computer. The original solution takes ~900 miliseconds.
But I would go with the recursive function anyway.
I was looking for a partition function which can split a large list to small amount of workers. With lkuty's partition you might get that one worker gets almost double work than all the others. If that's not what you want, here is a version which sublist lengths differ by at most 1.
Uses PropEr for testing.
%% #doc Split List into sub-lists so sub-lists lengths differ most by 1.
%% Does not preserve order.
-spec split_many(pos_integer(), [T]) -> [[T]] when T :: term().
split_many(N, List) ->
PieceLen = length(List) div N,
lists:reverse(split_many(PieceLen, N, List, [])).
-spec split_many(pos_integer(), pos_integer(), [T], [[T]]) ->
[[T]] when T :: term().
split_many(PieceLen, N, List, Acc) when length(Acc) < N ->
{Head, Tail} = lists:split(PieceLen, List),
split_many(PieceLen, N, Tail, [Head|Acc]);
split_many(_PieceLen, _N, List, Acc) ->
% Add an Elem to each list in Acc
{Appendable, LeaveAlone} = lists:split(length(List), Acc),
Appended = [[Elem|XS] || {Elem, XS} <- lists:zip(List, Appendable)],
lists:append(Appended, LeaveAlone).
Tests:
split_many_test_() ->
[
?_assertEqual([[1,2]], elibs_lists:split_many(1, [1,2])),
?_assertEqual([[1], [2]], elibs_lists:split_many(2, [1,2])),
?_assertEqual([[1], [3,2]], elibs_lists:split_many(2, [1,2,3])),
?_assertEqual([[1], [2], [4,3]], elibs_lists:split_many(3, [1,2,3,4])),
?_assertEqual([[1,2], [5,3,4]], elibs_lists:split_many(2, [1,2,3,4,5])),
?_assert(proper:quickcheck(split_many_proper1())),
?_assert(proper:quickcheck(split_many_proper2()))
].
%% #doc Verify all elements are preserved, number of groups is correct,
%% all groups have same number of elements (+-1)
split_many_proper1() ->
?FORALL({List, Groups},
{list(), pos_integer()},
begin
Split = elibs_lists:split_many(Groups, List),
% Lengths of sub-lists
Lengths = lists:usort(lists:map(fun erlang:length/1, Split)),
length(Split) =:= Groups andalso
lists:sort(lists:append(Split)) == lists:sort(List) andalso
length(Lengths) =< 2 andalso
case Lengths of
[Min, Max] -> Max == Min + 1;
[_] -> true
end
end
).
%% #doc If number of groups is divisable by number of elements, ordering must
%% stay the same
split_many_proper2() ->
?FORALL({Groups, List},
?LET({A, B},
{integer(1, 20), integer(1, 10)},
{A, vector(A*B, term())}),
List =:= lists:append(elibs_lists:split_many(Groups, List))
).
Here is a more general answer that works with any sublist size.
1> lists:foreach(fun(N) -> io:format("~2.10.0B -> ~w~n",[N, test:partition([1,2,3,4,5,6,7,8,9,10],N)] ) end, [1,2,3,4,5,6,7,8,9,10]).
01 -> [[1],[2],[3],[4],[5],[6],[7],[8],[9],[10]]
02 -> [[1,2],[3,4],[5,6],[7,8],[9,10]]
03 -> [[1,2,3],[4,5,6],[7,8,9],[10]]
04 -> [[1,2,3,4],[5,6,7,8],[10,9]]
05 -> [[1,2,3,4,5],[6,7,8,9,10]]
06 -> [[1,2,3,4,5,6],[10,9,8,7]]
07 -> [[1,2,3,4,5,6,7],[10,9,8]]
08 -> [[1,2,3,4,5,6,7,8],[10,9]]
09 -> [[1,2,3,4,5,6,7,8,9],[10]]
10 -> [[1,2,3,4,5,6,7,8,9,10]]
And the code to achieve this is stored inside a file called test.erl:
-module(test).
-compile(export_all).
partition(List, N) ->
partition(List, 1, N, []).
partition([], _C, _N, Acc) ->
lists:reverse(Acc) ;
partition([H|T], 1, N, Acc) ->
partition(T, 2, N, [[H]|Acc]) ;
partition([H|T], C, N, [HAcc|TAcc]) when C < N ->
partition(T, C+1, N, [[H|HAcc]|TAcc]) ;
partition([H|T], C, N, [HAcc|TAcc]) when C == N ->
partition(T, 1, N, [lists:reverse([H|HAcc])|TAcc]) ;
partition(L, C, N, Acc) when C > N ->
partition(L, 1, N, Acc).
It could probably be more elegant regarding the special case where C > N. Note that C is the size of the current sublist being constructed. At start, it is 1. And then it increments until it reaches the partition size of N.
We could also use a modified version of #chops code to let the last list contains the remaining items even if its size < N :
-module(n_length_chunks_fast).
-export([n_length_chunks_fast/2]).
n_length_chunks_fast(List,Len) ->
SkipLength = case length(List) rem Len of
0 -> 0;
N -> Len - N
end,
n_length_chunks_fast(lists:reverse(List),[],SkipLength,Len).
n_length_chunks_fast([],Acc,_Pos,_Max) -> Acc;
n_length_chunks_fast([H|T],Acc,Pos,Max) when Pos==Max ->
n_length_chunks_fast(T,[[H] | Acc],1,Max);
n_length_chunks_fast([H|T],[HAcc | TAcc],Pos,Max) ->
n_length_chunks_fast(T,[[H | HAcc] | TAcc],Pos+1,Max);
n_length_chunks_fast([H|T],[],Pos,Max) ->
n_length_chunks_fast(T,[[H]],Pos+1,Max).
I've slightly altered the implementation from #JLarky to remove the guard expression, which should be slightly faster:
split_list(List, Max) ->
element(1, lists:foldl(fun
(E, {[Buff|Acc], 1}) ->
{[[E],Buff|Acc], Max};
(E, {[Buff|Acc], C}) ->
{[[E|Buff]|Acc], C-1};
(E, {[], _}) ->
{[[E]], Max}
end, {[], Max}, List)).