How to convert this piece of elixir code to Erlang? - erlang

There is a piece of code in elixir that checks if node is alive or not and makes some operations on the basis of that. How to implement the same functionality in erlang?
def make_distributed([head | tail],l) do
unless Node.alive?() do
try do
{ip_tuple,_,_} = head
current_ip = to_string(:inet_parse.ntoa(ip_tuple))
if current_ip === "127.0.0.1" do
if l > 1 do
make_distributed(tail,l-1)
else
IO.puts "Could not make current node distributed."
end
else
server_node_name = String.to_atom("client#" <> current_ip)
Node.start(server_node_name)
Node.set_cookie(server_node_name,:monster)
Node.connect(String.to_atom("server#" <> current_ip))
end
rescue
_ -> if l > 1, do: make_distributed(tail,l-1), else: IO.puts "Could not make current node distributed."
end
end
end
if...else works differently in erlang, and I tried changing it to that format but some of the functonalities that are there in elixir code are hard to show in erlang.

First, a direct translation…
make_distributed([Head | Tail], L) ->
case node:is_alive() of
false ->
false; % Not sure what unless returns if the condition is false
true ->
try
{IPTuple, _, _} = Head,
CurrentIP = iolist_to_binary(inet_parse:ntoa(IPTuple)),
case CurrentIP of
<<"127.0.0.1">> ->
case L of
L when L > 1 ->
make_distributed(Tail, L - 1);
_ ->
io:format("Could not make current node distributed.")
end;
_ ->
ServerNodeName = binary_to_atom(<<"client#", CurrentIP/binary>>),
net_kernel:start([ServerNodeName, longnames, 15000]),
erlang:set_cookie(ServerNodeName, monster),
net_kernel:connect_node(binary_to_atom(<<"server#", CurrentIP/binary>>))
end
catch
_:_ ->
case L of
L when L > 1 ->
make_distributed(Tail, L - 1);
_ ->
io:format("Could not make current node distributed.")
end
end
end.
But that's some very ugly code. Let's use pattern-matching more effectively…
make_distributed([{IPTuple, _, _} | Tail], L) ->
case node:is_alive() of
false ->
false; % Not sure what unless returns if the condition is false
true ->
try
case iolist_to_binary(inet_parse:ntoa(IPTuple)) of
<<"127.0.0.1">> when L > 1 ->
make_distributed(Tail, L - 1);
<<"127.0.0.1">> ->
io:format("Could not make current node distributed.");
CurrentIP ->
ServerNodeName = binary_to_atom(<<"client#", CurrentIP/binary>>),
net_kernel:start([ServerNodeName, longnames, 15000]),
erlang:set_cookie(ServerNodeName, monster),
net_kernel:connect_node(binary_to_atom(<<"server#", CurrentIP/binary>>))
end
catch
_:_ when L > 1 ->
make_distributed(Tail, L - 1);
_:_ ->
io:format("Could not make current node distributed.")
end
end.
Still… we don't need to check for node liveness on every recursive step, right?
maybe_make_distributed(IPTuples, L) ->
case node:is_alive() of
false ->
false; % Not sure what unless returns if the condition is false
true ->
make_distributed(IPTuples, L)
end.
make_distributed([{IPTuple, _, _} | Tail], L) ->
try
case iolist_to_binary(inet_parse:ntoa(IPTuple)) of
<<"127.0.0.1">> when L > 1 ->
make_distributed(Tail, L - 1);
<<"127.0.0.1">> ->
io:format("Could not make current node distributed.");
CurrentIP ->
ServerNodeName = binary_to_atom(<<"client#", CurrentIP/binary>>),
net_kernel:start([ServerNodeName, longnames, 15000]),
erlang:set_cookie(ServerNodeName, monster),
net_kernel:connect_node(binary_to_atom(<<"server#", CurrentIP/binary>>))
end
catch
_:_ when L > 1 ->
make_distributed(Tail, L - 1);
_:_ ->
io:format("Could not make current node distributed.")
end.
Finally, let's move the L > 1 check to its own clause like a regular recursive function…
maybe_make_distributed(IPTuples, L) ->
case node:is_alive() of
false ->
false; % Not sure what unless returns if the condition is false
true ->
make_distributed(IPTuples, L)
end.
make_distributed(_, 0) ->
io:format("Could not make current node distributed.");
make_distributed([{IPTuple, _, _} | Tail], L) ->
try
case iolist_to_binary(inet_parse:ntoa(IPTuple)) of
<<"127.0.0.1">> ->
make_distributed(Tail, L - 1);
CurrentIP ->
ServerNodeName = binary_to_atom(<<"client#", CurrentIP/binary>>),
net_kernel:start([ServerNodeName, longnames, 15000]),
erlang:set_cookie(ServerNodeName, monster),
net_kernel:connect_node(binary_to_atom(<<"server#", CurrentIP/binary>>))
end
catch
_:_ ->
make_distributed(Tail, L - 1)
end.
Now that's kind of the code I would write in Erlang to achieve the same goal as your Elixir code.

Related

ejabberd two functions with same name

iam new in erlang and OTP i have two questions :
iam trying to read some erlang code from ejabberd source, it's very complex and iam surprised with two start/0 functions and two start/1 functions too in ejabberd_logger.erl so
1-what's this ? which function code will be executed when calling start/1?
2-should i read and understand all ejabberd source code to modify and customize it like i want ? i see that it's a huge code but is this a must for good handling ?
-module(ejabberd_logger).
-compile({no_auto_import, [get/0]}).
%% API
-export([start/0, get/0, set/1, get_log_path/0, flush/0]).
-export([convert_loglevel/1, loglevels/0]).
-ifndef(LAGER).
-export([progress_filter/2]).
-endif.
%% Deprecated functions
-export([restart/0, reopen_log/0, rotate_log/0]).
-deprecated([{restart, 0},
{reopen_log, 0},
{rotate_log, 0}]).
-type loglevel() :: none | emergency | alert | critical |
error | warning | notice | info | debug.
-define(is_loglevel(L),
((L == none) or (L == emergency) or (L == alert)
or (L == critical) or (L == error) or (L == warning)
or (L == notice) or (L == info) or (L == debug))).
-export_type([loglevel/0]).
%%%===================================================================
%%% API
%%%===================================================================
-spec get_log_path() -> string().
get_log_path() ->
case ejabberd_config:env_binary_to_list(ejabberd, log_path) of
{ok, Path} ->
Path;
undefined ->
case os:getenv("EJABBERD_LOG_PATH") of
false ->
"ejabberd.log";
Path ->
Path
end
end.
-spec loglevels() -> [loglevel(), ...].
loglevels() ->
[none, emergency, alert, critical, error, warning, notice, info, debug].
-spec convert_loglevel(0..5) -> loglevel().
convert_loglevel(0) -> none;
convert_loglevel(1) -> critical;
convert_loglevel(2) -> error;
convert_loglevel(3) -> warning;
convert_loglevel(4) -> info;
convert_loglevel(5) -> debug.
quiet_mode() ->
case application:get_env(ejabberd, quiet) of
{ok, true} -> true;
_ -> false
end.
-spec get_integer_env(atom(), T) -> T.
get_integer_env(Name, Default) ->
case application:get_env(ejabberd, Name) of
{ok, I} when is_integer(I), I>=0 ->
I;
{ok, infinity} ->
infinity;
undefined ->
Default;
{ok, Junk} ->
error_logger:error_msg("wrong value for ~ts: ~p; "
"using ~p as a fallback~n",
[Name, Junk, Default]),
Default
end.
-ifdef(LAGER).
-spec get_string_env(atom(), T) -> T.
get_string_env(Name, Default) ->
case application:get_env(ejabberd, Name) of
{ok, L} when is_list(L) ->
L;
undefined ->
Default;
{ok, Junk} ->
error_logger:error_msg("wrong value for ~ts: ~p; "
"using ~p as a fallback~n",
[Name, Junk, Default]),
Default
end.
-spec start() -> ok.
start() ->
start(info).
start(Level) ->
StartedApps = application:which_applications(5000),
case lists:keyfind(logger, 1, StartedApps) of
%% Elixir logger is started. We assume everything is in place
%% to use lager to Elixir logger bridge.
{logger, _, _} ->
error_logger:info_msg("Ignoring ejabberd logger options, using Elixir Logger.", []),
%% Do not start lager, we rely on Elixir Logger
do_start_for_logger(Level);
_ ->
do_start(Level)
end.
do_start_for_logger(Level) ->
application:load(sasl),
application:set_env(sasl, sasl_error_logger, false),
application:load(lager),
application:set_env(lager, error_logger_redirect, false),
application:set_env(lager, error_logger_whitelist, ['Elixir.Logger.ErrorHandler']),
application:set_env(lager, crash_log, false),
application:set_env(lager, handlers, [{elixir_logger_backend, [{level, Level}]}]),
ejabberd:start_app(lager),
ok.
do_start(Level) ->
application:load(sasl),
application:set_env(sasl, sasl_error_logger, false),
application:load(lager),
ConsoleLog = get_log_path(),
Dir = filename:dirname(ConsoleLog),
ErrorLog = filename:join([Dir, "error.log"]),
CrashLog = filename:join([Dir, "crash.log"]),
LogRotateDate = get_string_env(log_rotate_date, ""),
LogRotateSize = case get_integer_env(log_rotate_size, 10*1024*1024) of
infinity -> 0;
V -> V
end,
LogRotateCount = get_integer_env(log_rotate_count, 1),
LogRateLimit = get_integer_env(log_rate_limit, 100),
ConsoleLevel0 = case quiet_mode() of
true -> critical;
_ -> Level
end,
ConsoleLevel = case get_lager_version() >= "3.6.0" of
true -> [{level, ConsoleLevel0}];
false -> ConsoleLevel0
end,
application:set_env(lager, error_logger_hwm, LogRateLimit),
application:set_env(
lager, handlers,
[{lager_console_backend, ConsoleLevel},
{lager_file_backend, [{file, ConsoleLog}, {level, Level}, {date, LogRotateDate},
{count, LogRotateCount}, {size, LogRotateSize}]},
{lager_file_backend, [{file, ErrorLog}, {level, error}, {date, LogRotateDate},
{count, LogRotateCount}, {size, LogRotateSize}]}]),
application:set_env(lager, crash_log, CrashLog),
application:set_env(lager, crash_log_date, LogRotateDate),
application:set_env(lager, crash_log_size, LogRotateSize),
application:set_env(lager, crash_log_count, LogRotateCount),
ejabberd:start_app(lager),
lists:foreach(fun(Handler) ->
lager:set_loghwm(Handler, LogRateLimit)
end, gen_event:which_handlers(lager_event)).
-spec restart() -> ok.
restart() ->
Level = ejabberd_option:loglevel(),
application:stop(lager),
start(Level).
-spec reopen_log() -> ok.
reopen_log() ->
ok.
-spec rotate_log() -> ok.
rotate_log() ->
catch lager_crash_log ! rotate,
lists:foreach(
fun({lager_file_backend, File}) ->
whereis(lager_event) ! {rotate, File};
(_) ->
ok
end, gen_event:which_handlers(lager_event)).
-spec get() -> loglevel().
get() ->
Handlers = get_lager_handlers(),
lists:foldl(fun(lager_console_backend, _Acc) ->
lager:get_loglevel(lager_console_backend);
(elixir_logger_backend, _Acc) ->
lager:get_loglevel(elixir_logger_backend);
(_, Acc) ->
Acc
end,
none, Handlers).
-spec set(0..5 | loglevel()) -> ok.
set(N) when is_integer(N), N>=0, N=<5 ->
set(convert_loglevel(N));
set(Level) when ?is_loglevel(Level) ->
case get() of
Level ->
ok;
_ ->
ConsoleLog = get_log_path(),
QuietMode = quiet_mode(),
lists:foreach(
fun({lager_file_backend, File} = H) when File == ConsoleLog ->
lager:set_loglevel(H, Level);
(lager_console_backend = H) when not QuietMode ->
lager:set_loglevel(H, Level);
(elixir_logger_backend = H) ->
lager:set_loglevel(H, Level);
(_) ->
ok
end, get_lager_handlers())
end,
case Level of
debug -> xmpp:set_config([{debug, true}]);
_ -> xmpp:set_config([{debug, false}])
end.
get_lager_handlers() ->
case catch gen_event:which_handlers(lager_event) of
{'EXIT',noproc} ->
[];
Result ->
Result
end.
-spec get_lager_version() -> string().
get_lager_version() ->
Apps = application:loaded_applications(),
case lists:keyfind(lager, 1, Apps) of
{_, _, Vsn} -> Vsn;
false -> "0.0.0"
end.
-spec flush() -> ok.
flush() ->
application:stop(lager),
application:stop(sasl).
-else.
-include_lib("kernel/include/logger.hrl").
-spec start() -> ok | {error, term()}.
start() ->
start(info).
start(Level) ->
EjabberdLog = get_log_path(),
Dir = filename:dirname(EjabberdLog),
ErrorLog = filename:join([Dir, "error.log"]),
LogRotateSize = get_integer_env(log_rotate_size, 10*1024*1024),
LogRotateCount = get_integer_env(log_rotate_count, 1),
Config = #{max_no_bytes => LogRotateSize,
max_no_files => LogRotateCount,
filesync_repeat_interval => no_repeat,
file_check => 1000,
sync_mode_qlen => 1000,
drop_mode_qlen => 1000,
flush_qlen => 5000},
FmtConfig = #{legacy_header => false,
time_designator => $ ,
max_size => 100*1024,
single_line => false},
FileFmtConfig = FmtConfig#{template => file_template()},
ConsoleFmtConfig = FmtConfig#{template => console_template()},
try
ok = logger:set_primary_config(level, Level),
DefaultHandlerId = get_default_handlerid(),
ok = logger:update_formatter_config(DefaultHandlerId, ConsoleFmtConfig),
case quiet_mode() of
true ->
ok = logger:set_handler_config(DefaultHandlerId, level, critical);
_ ->
ok
end,
case logger:add_primary_filter(progress_report,
{fun ?MODULE:progress_filter/2, stop}) of
ok -> ok;
{error, {already_exist, _}} -> ok
end,
case logger:add_handler(ejabberd_log, logger_std_h,
#{level => all,
config => Config#{file => EjabberdLog},
formatter => {logger_formatter, FileFmtConfig}}) of
ok -> ok;
{error, {already_exist, _}} -> ok
end,
case logger:add_handler(error_log, logger_std_h,
#{level => error,
config => Config#{file => ErrorLog},
formatter => {logger_formatter, FileFmtConfig}}) of
ok -> ok;
{error, {already_exist, _}} -> ok
end
catch _:{Tag, Err} when Tag == badmatch; Tag == case_clause ->
?LOG_CRITICAL("Failed to set logging: ~p", [Err]),
Err
end.
get_default_handlerid() ->
Ids = logger:get_handler_ids(),
case lists:member(default, Ids) of
true -> default;
false -> hd(Ids)
end.
-spec restart() -> ok.
restart() ->
ok.
progress_filter(#{level:=info,msg:={report,#{label:={_,progress}}}} = Event, _) ->
case get() of
debug ->
logger_filters:progress(Event#{level => debug}, log);
_ ->
stop
end;
progress_filter(Event, _) ->
Event.
console_template() ->
[time, " [", level, "] " | msg()].
file_template() ->
[time, " [", level, "] ", pid,
{mfa, ["#", mfa, {line, [":", line], []}], []}, " " | msg()].
msg() ->
[{logger_formatter, [[logger_formatter, title], ":", io_lib:nl()], []},
msg, io_lib:nl()].
-spec reopen_log() -> ok.
reopen_log() ->
ok.
-spec rotate_log() -> ok.
rotate_log() ->
ok.
-spec get() -> loglevel().
get() ->
#{level := Level} = logger:get_primary_config(),
Level.
-spec set(0..5 | loglevel()) -> ok.
set(N) when is_integer(N), N>=0, N=<5 ->
set(convert_loglevel(N));
set(Level) when ?is_loglevel(Level) ->
case get() of
Level -> ok;
PrevLevel ->
?LOG_NOTICE("Changing loglevel from '~s' to '~s'",
[PrevLevel, Level]),
logger:set_primary_config(level, Level),
case Level of
debug -> xmpp:set_config([{debug, true}]);
_ -> xmpp:set_config([{debug, false}])
end
end.
-spec flush() -> ok.
flush() ->
lists:foreach(
fun(#{id := HandlerId, module := logger_std_h}) ->
logger_std_h:filesync(HandlerId);
(#{id := HandlerId, module := logger_disk_log_h}) ->
logger_disk_log_h:filesync(HandlerId);
(_) ->
ok
end, logger:get_handler_config()).
-endif.
Here, the first version is protected by -ifdef(LAGER). and thus gets used if the macro LAGER is defined, and the second version comes after '-else.', and gets used if LAGER is not defined. (The first version uses the Lager library for logging, and the second version uses the newer built-in logger library.)

Why is badarg displayed in Erlang?

This is my Erlang code:
-module(solarSystem).
-export([process_csv/1,is_numeric/1]).
is_numeric(L) ->
S = trim(L,""),
Float = (catch erlang:list_to_float( S)),
Int = (catch erlang:list_to_integer(S)),
is_number(Float) orelse is_number(Int).
trim([],A)->A;
trim([32|T],A)->trim(T,A);
trim([H|T],A)->trim(T,A++[H]).
process_csv([_H|T]) -> process_line(T, "").
process_line([], A) -> A;
process_line([H|T], A) ->
process_line(T, A ++ deal(H, "", 1)).
deal([], B, _Count) -> B ++ "\n";
deal([H|T], B, Count) ->
if (H == "planets ") ->
deal([], planetcheck([H|T], "", 1), Count);
true ->
case is_numeric(H) of
true -> if Count == 6 ->
deal([], B ++ subcheck([H|T], "", 6) ++ "];", Count+1);
true ->
deal(T, B ++ H ++ ",", Count+1)
end;
false -> if Count == 2 ->
deal(T, B ++ "=[\"" ++ H ++ "\",", Count+1);
true ->
deal(T, B ++ H, Count+1)
end
end
end.
subcheck([], C, _Scount) -> C;
subcheck([H|T], C, Scount) ->
case is_numeric(H) of
true -> if (Scount == 6) and (T == []) ->
subcheck(T, C ++ H ++ ",[]", Scount+1);
true ->
subcheck([], C ++ H ++ "," ++ noone(T, C), Scount+1)
end;
false -> if T == [] ->
subcheck(T, C ++ H ++ "]", Scount+1);
true ->
subcheck(T, C ++ H ++ ",", Scount+1)
end
end.
noone([], D) -> D;
noone([H|T], D) ->
case is_numeric(H) of
true ->
noone([], D ++ T);
false ->
if T == [] ->
subcheck(T, D ++ "["++ H, 7);
true ->
subcheck(T, D ++ "["++ H ++ ",", 7)
end
end.
planetcheck([], E, _Pcount) -> E ++ "];";
planetcheck([H|T], E, Pcount) ->
if Pcount == 1 ->
planetcheck(T, E ++ H ++ "=[", Pcount+1);
true ->
if T == "" ->
planetcheck(T, E ++ H, Pcount+1);
true ->
planetcheck(T, E ++ H ++ ",", Pcount+1)
end
end.
This is the main code (another file which will run my code):
#!/usr/bin/env escript
%-module(main).
%-export([main/1, print_list/1, usage/0]).
%% -*- erlang -*-
%%! -smp enable -sname factorial -mnesia debug verbose
main([String]) ->
try
CSV = csv:parse_file(String),
F =solarSystem:process_csv(CSV),
print_list(F)
catch
A:B -> io:format("~w : ~w~n",[A,B]),
usage()
end;
main(_) ->
usage().
print_list([])-> [];
print_list([H|T])->io:format("~s~n",[H]),print_list(T).
usage() ->
io:format("usage: escript main.erl <filename>\n"),
halt(1).
The error was caused by reopening VSCode, but the error didn't appear yesterday while I opened a previous one. I am able to run it without compiling the main function before I close VSCode. The image shows the error.
Your use of try and catch is hiding the error. Remove it and Erlang will tell you exactly where the error's coming from. If you don't want to do that, use catch A:B:S. S is the stack trace; print that out.

Erlang sumif function

I'm trying to make a sumif function in Erlang that would return a sum of all elements in a list if the predicate function evaluates to true. Here is what I have:
sumif(_, []) -> undefined;
sumif(Fun, [H|T]) -> case Fun(H) of
true -> H + sumif(Fun, T);
false -> sumif(Fun, T)
end.
I also implemented my own pos function which returns true if a number is greater than 0 and false otherwise:
pos(A) -> A > 0.
I tried using pos with sumif but I'm getting this error:
exception error: bad function pos
Why is this happening? Is it because of my sumif function or pos? I have tested pos on its own and it seems to work just fine.
Edit: It might be because how I'm calling the function. This is how I'm currently calling it: hi:sumif(pos,[-1,1,2,-3]). Where hi is my module name.
Is it because of my sumif function or pos?
It's because of sumif. You should return 0 when an empty list is passed, as it'll be called from the 2nd clause when T is []:
-module(a).
-compile(export_all).
sumif(_, []) -> 0;
sumif(Fun, [H|T]) -> case Fun(H) of
true -> H + sumif(Fun, T);
false -> sumif(Fun, T)
end.
pos(A) -> A > 0.
Test:
1> c(a).
{ok,a}
2> a:sumif(fun a:pos/1, [-4, -2, 0, 2, 4]).
6
List comprehensions make things far simpler:
sumif(F, L) ->
lists:sum([X || X <- L, F(X)]).
Dobert's answer is of cousrse right, problem is your sum for empty list.
If your concern is performance a little bit you should stick to tail recursive solution (in this case it matter because there is not lists:reverse/1 involved).
sumif(F, L) ->
sumif(F, L, 0).
sumif(F, [], Acc) when is_function(F, 1) -> Acc;
sumif(F, [H|T], Acc) ->
New = case F(H) of
true -> H+Acc;
false -> Acc
end,
sumif(F, T, New).
Ways how to make correct function for first parameter:
F1 = fun pos/1, % inside module where pos/1 defined
F2 = fun xyz:pos/1, % exported function from module xyz (hot code swap works)
N = 0,
F3 = fun(X) -> X > N end, % closure
% test it
true = lists:all(fun(F) -> is_function(F, 1) end, [F1, F2, F3]).
There has tow error in your code:
1. sumif(_, []) -> undefined; should return 0, not undefined.
2. when you pass pos(A) -> A > 0. to sumif/2,you should use fun pos/1, please read http://erlang.org/doc/programming_examples/funs.html#id59138
sumif(F, L) ->
lists:foldl(fun(X, Sum) when F(X) -> Sum+X; (_) -> Sum end, 0, L).
You can use lists:foldl.

erlang; outsmarting compiler with memoization?

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.

version compare function, about special character

I am studying rabbitmq source code now for learning erlang technique.
The following is from rabbit_misc.erl file. The purpose is to check application's minimum version.
In the 5th and 7th sub sentance of version_compare/N, there is is a special character, which is $0. But I don't know how it happens?
My reason that it will not happens is that in the last sentance, after lists:splitwith/N, AT1 and BT1 will be started with "$.".
version_compare(A, B, lte) ->
case version_compare(A, B) of
eq -> true;
lt -> true;
gt -> false
end;
version_compare(A, B, gte) ->
case version_compare(A, B) of
eq -> true;
gt -> true;
lt -> false
end;
version_compare(A, B, Result) ->
Result =:= version_compare(A, B).
version_compare(A, A) ->
eq;
version_compare([], [$0 | B]) ->
version_compare([], dropdot(B));
version_compare([], _) ->
lt; %% 2.3 < 2.3.1
version_compare([$0 | A], []) ->
version_compare(dropdot(A), []);
version_compare(_, []) ->
gt; %% 2.3.1 > 2.3
version_compare(A, B) ->
{AStr, ATl} = lists:splitwith(fun (X) -> X =/= $. end, A),
{BStr, BTl} = lists:splitwith(fun (X) -> X =/= $. end, B),
ANum = list_to_integer(AStr),
BNum = list_to_integer(BStr),
if ANum =:= BNum -> version_compare(dropdot(ATl), dropdot(BTl));
ANum < BNum -> lt;
ANum > BNum -> gt
end.
$0 is not a special character -- this is zero string: "0".
Versions may be complex: 0.1.22.333 and splitwith/2 splits into head and tail ("0" and ".1.22.333").
I imagine that handling $0 is for cases like "1.0.0" and "1"
{"1",".0.0"} vs {"1",[]}

Resources