The following is my solution to Project Euler 14, which works (in 18 s):
%Which starting number, under one million, produces the longest Collartz chain?
-module(soln14).
-export([solve/0]).
collatz(L) ->
[H|T] = L,
F = erlang:get({'collatz', H}),
case is_list(F) of
true ->
R = lists:append(F, T);
false ->
if H == 1 ->
R = L;
true ->
if H rem 2 == 0 ->
R = collatz([H div 2 | L]);
true ->
R = collatz([3*H+1 | L])
end
end,
erlang:put({'collatz', lists:last(L)}, R),
R
end.
dosolve(N, Max, MaxN, TheList) ->
if N == 1000000 -> MaxN;
true ->
L = collatz([N]),
M = length(L),
if M > Max -> dosolve(N+1, M, N, L);
true ->
dosolve(N+1, Max, MaxN, TheList)
end
end.
solve() ->
{Megass, Ss, Micros} = erlang:timestamp(),
S = dosolve(1, -1, 1, []),
{Megase, Se, Microe} = erlang:timestamp(),
{Megase-Megass, Se-Ss, Microe-Micros, S}.
However, the compiler complains:
8> c(soln14).
soln14.erl:20: Warning: variable 'R' is unused
{ok,soln14}
9> soln14:solve().
{0,18,-386776,837799}
Is this a compiler scoping error, or do I have a legit bug?
It's not a compiler error, just a warning that in the true case of "case is_list(F) of", the bindning of R to the result of lists:append() is pointless, since this value of R will not be used after that point, just returned immediately. I'll leave it to you to figure out if that's a bug or not. It may be that you are fooled by your indentation. The lines "erlang:put(...)," and "R" are both still within the "false" case of "case is_list(F) of", and should be deeper indented to reflect this.
The error message and the code are not "synchronized". with the version you give, the warning is on line 10: R = lists:append(F, T);.
What it means is that you bind the result of the lists:append/2 call to R and that you don't use it later in the true statement.
this is not the case in the false statement since you use R in the function erlang:put/2.
You could write the code this way:
%Which starting number, under one million, produces the longest Collartz chain?
-module(soln14).
-export([solve/0,dosolve/4]).
collatz(L) ->
[H|T] = L,
F = erlang:get({'collatz', H}),
case is_list(F) of
true ->
lists:append(F, T);
false ->
R = if H == 1 ->
L;
true ->
if H rem 2 == 0 ->
collatz([H div 2 | L]);
true ->
collatz([3*H+1 | L])
end
end,
erlang:put({'collatz', lists:last(L)}, R),
R
end.
dosolve(N, Max, MaxN, TheList) ->
if N == 1000000 -> MaxN;
true ->
L = collatz([N]),
M = length(L),
if M > Max -> dosolve(N+1, M, N, L);
true ->
dosolve(N+1, Max, MaxN, TheList)
end
end.
solve() ->
timer:tc(?MODULE,dosolve,[1, -1, 1, []]).
Warning the code uses a huge amount of memory, collatz is not tail recursive, and it seems that there is some garbage collecting witch is not done.
The more formal definition of problem is
Write a function map_search_pred(Map, Pred) that returns the first
element {Key,Value} in the map for which Pred(Key, Value) is true.
My attempt, looks like
map_search_pred(Map, Pred) ->
M = [{Key, Value} || {Key, Value} <- maps:to_list(Map), Pred(Key, Value) =:= true],
case length(M) of
0 -> {};
_ -> lists:nth(1, M)
end.
When I run this, I get correct answer
1> lib_misc:map_search_pred(#{}, fun(X, Y) -> X =:= Y end).
{}
2> lib_misc:map_search_pred(#{1 => 1, 2 => 2}, fun(X, Y) -> X =:= Y end).
{1,1}
3> lib_misc:map_search_pred(#{1 => 1, 2 => 3}, fun(X, Y) -> X =:= Y end).
{1,1}
4> lib_misc:map_search_pred(#{1 => 2, 2 => 2}, fun(X, Y) -> X =:= Y end).
{2,2}
5>
Problem?
The problem is efficiency.
It uses list comprehension and runs for all the elements in the list. The better solution would be to return after the first match.
As a beginner to language, I do not know a better idiomatic way to solve this, so looking for better ideas and suggestions
You could walk the list yourself and return as soon as you find an element for which Pred(Key, Value) is true:
map_search_pred(Map, Pred) when is_map(Map) ->
map_search_pred(maps:to_list(Map), Pred);
map_search_pred([], _) ->
error;
map_search_pred([{Key,Value}=H|T], Pred) ->
case Pred(Key, Value) of
true ->
{ok, H};
false ->
map_search_pred(T, Pred)
end.
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.
I'm learning Erlang and decided to implement bubble sort in it, it took me some effort and in result i've succeded, but i see that my way of thinking is incorrect, is there more effecient way to implement it or not?
bubble_sort(L) ->
if
length(L) > 1 ->
SL=bubble_sort_p(L),
bubble_sort(lists:sublist(SL,1,length(SL)-1)) ++ [lists:last(SL)];
true -> L
end.
bubble_sort_p([]) -> [];
bubble_sort_p([F | R]) ->
case length(R) > 0 of
true -> case F > hd(R) of
true -> [hd(R)] ++ bubble_sort_p([F|tl(R)]);
false -> [F] ++ bubble_sort_p([hd(R)|tl(R)])
end;
false -> [F]
end.
The implementation from the top of my head would be:
bubble_sort(L) -> bubble_sort(L, [], false).
bubble_sort([A, B | T], Acc, _) when A > B ->
bubble_sort([A | T], [B | Acc], true);
bubble_sort([A, B | T], Acc, Tainted) ->
bubble_sort([B | T], [A | Acc], Tainted);
bubble_sort([A | T], Acc, Tainted) ->
bubble_sort(T, [A | Acc], Tainted);
bubble_sort([], Acc, true) ->
bubble_sort(lists:reverse(Acc));
bubble_sort([], Acc, false) ->
lists:reverse(Acc).
If we are talking efficiency, we obvioulsy should not go for bubble sort in the first place.
Allow me to re-write your code in a much more readable fashion (without changing anything about the semantics):
bubble_sort(L) when length(L) =< 1 ->
L;
bubble_sort(L) ->
SL = bubble_sort_p(L),
bubble_sort(lists:sublist(SL,1,length(SL)-1)) ++ [lists:last(SL)].
bubble_sort_p([]) ->
[];
bubble_sort_p([F]) ->
[F];
bubble_sort_p([F,G|T]) when F > G ->
[G|bubble_sort_p([F|T])];
bubble_sort_p([F,G|T]) ->
[F|bubble_sort_p([G|T])].
This should allow for much easier contemplation of the things actually going on in the program.
And employing lists:foldr:
bubble(List) ->
Step = fun
(E,{S,[]}) -> {S,[E]};
(E,{_,[X|XS]}) when E > X -> {swapped,[X|[E|XS]]};
(E,{S,XS}) -> {S,[E|XS]}
end,
case lists:foldr(Step, {intact,[]}, List) of
{intact,XS} -> XS;
{swapped,XS} -> bubble(XS)
end.
There is an answer at http://en.literateprograms.org/Bubble_sort_%28Erlang%29
-module(bubblesort).
-export([sort/1]).
-import(lists, [reverse/1]).
sort(L) -> sort(L, [], true).
sort([], L, true) -> reverse(L);
sort([], L, false) -> sort(reverse(L), [], true);
sort([ X, Y | T ], L, _) when X > Y ->
sort([ X | T ], [ Y | L ], false);
sort([ X | T ], L, Halt) -> sort(T, [ X | L ], Halt).
An example for sorting 2,4,3,5,1
sort([2,4,3,5,1])
round 1
=> sort([2,4,3,5,1], [], true)
=> sort([4,3,5,1], [2], true)
=> sort([4,5,1], [3,2], false)
=> sort([5,1], [4,3,2], false)
=> sort([5], [1,4,3,2], false)
=> sort([], [5,1,4,3,2], false)
=> sort([2,3,4,1,5], [], true)
round 2
=> sort([3,4,1,5], [2], true)
=> sort([4,1,5], [3,2], true)
=> sort([4,5], [1,3,2], false)
=> sort([5], [4,1,3,2], false)
=> sort([], [5,4,1,3,2], false)
=> sort([2,3,1,4,5], [], true)
round 3
=> sort([3,1,4,5], [2], true)
=> sort([3,4,5], [1,2], false)
=> sort([4,5], [3,1,2], false)
=> sort([5], [4,3,1,2], false)
=> sort([], [5,4,3,1,2], false)
=> sort([2,1,3,4,5], true)
round 4
=> sort([2,3,4,5], [1], false)
=> sort([3,4,5], [2,1], false)
=> sort([4,5], [3,2,1], false)
=> sort([5], [4,3,2,1], false)
=> sort([], [5,4,3,2,1], false)
=> sort([1,2,3,4,5], [], true)
round 5
=> sort([2,3,4,5], [1], true)
=> sort([3,4,5],[2,1], true)
=> sort([4,5],[3,2,1], true)
=> sort([5],[4,3,2,1], true)
=> sort([], [5,4,3,2,1], true)
=> [1,2,3,4,5]
-module(bubbleSort).
-compile(export_all).
-include_lib("eunit/include/eunit.hrl").
sort(L) ->
sort(L, length(L), []).
sort(_L, 0, _Res) -> [];
sort([H | _T], 1, Res) -> [H | Res];
sort(L, Len, Res) ->
T1 = lists:sublist(L, 1, Len),
T2 = inner_sort(T1, []),
Last = lists:last(T2),
sort(T2, Len - 1, [Last | Res]).
inner_sort([A, B], Res) when (A < B)->
Res ++ [A, B];
inner_sort([A, B], Res) ->
Res ++ [B, A];
inner_sort([A, B | T], Res) when (A < B) ->
inner_sort([B | T], Res ++ [A]);
inner_sort([A, B | T], Res) ->
inner_sort([A | T], Res ++ [B]).
test()->
L = [5, 3, -1, 10, 6, 100, 99],
?assert(sort([]) =:= []),
?assert(sort([1]) =:= [1]),
?assert(sort([1, 2, 3, 4]) =:= [1, 2, 3, 4]),
?assert(sort([10, 5, 3, 2, 1]) =:= [1, 2, 3, 5, 10]),
?assert(sort(L) =:= [-1, 3, 5, 6, 10, 99, 100]).
Easy to read & understand implementation w ascending sort direction.
Tail recursion employed, computational complexity & memory usage are classic.
bsort([]) -> [];
bsort([H|T]) -> bsort([H|T],[]).
bsort([],[]) -> [];
bsort([],[H|T]) -> [H|T];
bsort([A|B], Sorted) when is_list(Sorted) -> M = lists:max([A|B]), bsort([A|B] -- [M], [M] ++ Sorted).