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.
Related
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.
-module(tut).
-export([main/0]).
main() ->
folders("C:/Users/David/test/").
folders(PATH) ->
{_,DD} = file:list_dir(PATH),
A = [{H,filelib:is_dir(PATH ++ H)}|| H <-DD],
% R is a list of all folders inside PATH
R = [PATH++X|| {X,Y} <- A, Y =:= true],
io:fwrite("~p~n", [R]),
case R of
[] -> ok;
% How call again folders function with the first element of the list?
% And save the result in some kind of structure
end.
Sorry for the beginner question, but I'm still new to Erlang. I would like to know how I can call the function again until saves the results in a kind of list, tuple or structure...
Like:
[
{"C:/Users/David/test/log",
{"C:/Users/David/test/log/a", "C:/Users/David/test/log/b"}},
{"C:/Users/David/test/logb",
{"C:/Users/David/test/logb/1", "C:/Users/David/test/logb/2","C:/Users/David/test/logb/3"}},
]
Few things:
These 2 calls can be simplified.
A = [{H,filelib:is_dir(PATH ++ H)}|| H <-DD],
R = [PATH++X|| {X,Y} <- A, Y =:= true],
into
A = [H || H <- DD, filelib:is_dir(PATH ++ H) =:= true],
In terms of representation, sub-folders should be in list format, not tuple. It will be difficult to work with if they were tuples.
Sample structure: {Folder, [Subfolder1, Subfolder2, ...]}, where SubfolderX will have the same definition and structure, recursively.
Folders are like tree, so need to have recursive call here. Hope you are already familiar with the concept. Below is one way to do it using list comprehension - there are other ways anyway, e.g. by using lists:foldl function.
folders(PATH) ->
{_, DD} = file:list_dir(PATH),
A = [H || H <- DD, filelib:is_dir(PATH ++ "/" ++ H) =:= true],
%%io:format("Path: ~p, A: ~p~n", [Path, A]),
case A of
[] -> %%Base case, i.e. folder has no sub-folders -> stop here
{PATH, []};
_ -> %%Recursive case, i.e. folder has sub-folders -> call #folders
{PATH, [folders(PATH ++ "/" ++ H2) || H2 <- A]}
end.
For consistency reason, you need to call the main function without a forward slash at the end, as this will be added in the function itself.
Folders = folders("C:/Users/David/test"). %% <- without forward slash
A helper function pretty_print below can be used to visualize the output on the Erlang shell
Full code:
-export([folders/1]).
-export([main/0]).
main() ->
Folders = folders("C:/Users/David/test"),
pretty_print(Folders, 0),
ok.
folders(PATH) ->
{_, DD} = file:list_dir(PATH),
A = [H || H <- DD, filelib:is_dir(PATH ++ "/" ++ H) =:= true], %%please note the "/" is added here
%%io:format("Path: ~p, A: ~p~n", [Path, A]),
case A of
[] -> %%Base case, i.e. folder has no sub-folders -> stop here
{PATH, []};
_ -> %%Recursive case, i.e. folder has sub-folders -> call #folders
{PATH, [folders(PATH ++ "/" ++ H2) || H2 <- A]}
end.
pretty_print(Folders, Depth) ->
{CurrrentFolder, ListSubfolders} = Folders,
SignTemp = lists:duplicate(Depth, "-"),
case Depth of
0 -> Sign = SignTemp;
_ -> Sign = "|" ++ SignTemp
end,
io:format("~s~s~n", [Sign, CurrrentFolder]),
[pretty_print(Subfolder, Depth+1) || Subfolder <- ListSubfolders].
In a recent question, I asked about the following
parsec parser:
manyLength
:: forall s u m a. ParsecT s u m a -> ParsecT s u m Int
manyLength p = go 0
where
go :: Int -> ParsecT s u m Int
go !i = (p *> go (i + 1)) <|> pure i
This function is similar to many. However, instead of returning [a], it
returns the number of times it was able to successfully run p.
This works well, except for one problem. It doesn't run in constant heap
space.
In the linked question, Li-yao
Xia gives an alternative way of
writing manyLength that uses constant heap space:
manyLengthConstantHeap
:: forall s u m a. ParsecT s u m a -> ParsecT s u m Int
manyLengthConstantHeap p = go 0
where
go :: Int -> ParsecT s u m Int
go !i =
((p *> pure True) <|> pure False) >>=
\success -> if success then go (i+1) else pure i
This is a significant improvement, but I don't understand why
manyLengthConstantHeap uses constant heap space, while my original manyLength doesn't.
If you inline (<|>) in manyLength, it looks somewhat like this:
manyLengthInline
:: forall s u m a. Monad m => ParsecT s u m a -> ParsecT s u m Int
manyLengthInline p = go 0
where
go :: Int -> ParsecT s u m Int
go !i =
ParsecT $ \s cok cerr eok eerr ->
let meerr :: ParserError -> m b
meerr err =
let neok :: Int -> State s u -> ParserError -> m b
neok y s' err' = eok y s' (mergeError err err')
neerr :: ParserError -> m b
neerr err' = eerr $ mergeError err err'
in unParser (pure i) s cok cerr neok neerr
in unParser (p *> go (i + 1)) s cok cerr eok meerr
If you inline (>>=) in manyLengthConstantHeap, it looks somewhat like this:
manyLengthConstantHeapInline
:: forall s u m a. Monad m => ParsecT s u m a -> ParsecT s u m Int
manyLengthConstantHeapInline p = go 0
where
go :: Int -> ParsecT s u m Int
go !i =
ParsecT $ \s cok cerr eok eerr ->
let mcok :: Bool -> State s u -> ParserError -> m b
mcok success s' err =
let peok :: Int -> State s u -> ParserError -> m b
peok int s'' err' = cok int s'' (mergeError err err')
peerr :: ParserError -> m b
peerr err' = cerr (mergeError err err')
in unParser
(if success then go (i + 1) else pure i)
s'
cok
cerr
peok
peerr
meok :: Bool -> State s u -> ParserError -> m b
meok success s' err =
let peok :: Int -> State s u -> ParserError -> m b
peok int s'' err' = eok int s'' (mergeError err err')
peerr :: ParserError -> m b
peerr err' = eerr (mergeError err err')
in unParser
(if success then go (i + 1) else pure i)
s'
cok
pcerr
peok
peerr
in unParser ((p *> pure True) <|> pure False) s mcok cerr meok eerr
Here is the ParsecT constructor for completeness:
newtype ParsecT s u m a = ParsecT
{ unParser
:: forall b .
State s u
-> (a -> State s u -> ParseError -> m b) -- consumed ok
-> (ParseError -> m b) -- consumed err
-> (a -> State s u -> ParseError -> m b) -- empty ok
-> (ParseError -> m b) -- empty err
-> m b
}
Why does manyLengthConstantHeap run with constant heap space, while
manyLength does not? It doesn't look like the recursive call to go is in
the tail-call position for either manyLengthConstantHeap or manyLength.
When writing parsec parsers in the future, how can I know the space
requirements for a given parser? How did Li-yao Xia know that
manyLengthConstantHeap would be okay?
I don't feel like I have any confidence in predicting which parsers will use a
lot of memory on a large input.
Is there an easy way to figure out whether a given function will be
tail-recursive in Haskell without running it? Or better yet, without compiling
it?
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.
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",[]}