-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].
The following source doesn't compile because Stopover is unbound.
I'm new to Erlang, how can I rewrite it?
-module(distances).
-export([ path/2 ]).
path( madrid, paris ) ->
{ km, 1049 };
path( paris, moscou ) ->
{ km, 2482 };
path( moscou, berlin ) ->
{ km, 1603 };
path( From, To ) ->
path( From, Stopover ) + path( Stopover, To ).
The usage of this module maybe:
path( madrid, moscou ).
And the epected answer should be { km, 3531}.
The following source doesn't compile because Stopover is unbound.
I'm new to Erlang, how can I rewrite it?
Look at this code:
-module(a).
-compile(export_all).
do_stuff() ->
Stopover.
Here's what happens when I try to compile it:
a.erl:5: variable 'Stopover' is unbound
The variable Stopover was never assigned a value, so erlang has no idea what should be returned by the function do_stuff(). You are doing something similar here:
path( From, Stopover ) + path( Stopover, To ).
The variables From and To are parameter variables for the function path(), and when path() is called, e.g. path(madrid, moscow), then madrid will be assigned to the variable From, and moscow will be assigned to the variable To. Note, however, that nowhere do you assign any value to the variable Stopover.
You need to redefine path() to look like this:
path(From, To, Stopover) ->
Next, you should try to see if adding tuples actually works:
2> {km, 5} + {km, 3}.
** exception error: an error occurred when evaluating an arithmetic expression
in operator +/2
called as {km,5} + {km,3}
3>
Nope!
What you need to do is use pattern matching to extract the distance, an integer, from each tuple, then add the two integers:
{km, Distance1} = path( From, Stopover ),
... = path(Stopover, To),
{km, Distance1 + Distance2}.
This question is already answered by #7stud, and I was wondering how to implement such a path search in erlang. Here is a possible solution:
-module(distances).
-export([ path/2,getTowns/0,start/1, stop/0 ]).
path(From,To) ->
Paths = getPath(),
path(From,To,maps:get(orderedTuple(From,To), Paths, not_found),Paths).
% distanceServer in charge to keep the liste of known distances
% server interfaces
start(Towns) ->
{ok,List} = file:consult(Towns),
Paths = lists:foldl(fun({A,B,D},Acc) -> maps:put(orderedTuple(A,B), D, Acc) end,#{},List),
start(Paths,distance_server).
stop() ->
distance_server ! stop.
getTowns() ->
K = maps:keys(getPath()),
L = lists:usort(lists:flatten([[A,B] || {A,B} <- K])),
io:format("list of towns :~n~p~n~n",[L]).
getPath() ->
distance_server ! {getPath,self()},
receive
Path -> Path
end.
% server fuctions
start(Paths,Server) ->
Pid = spawn(fun() -> distanceServer(Paths) end),
register(Server, Pid).
distanceServer(Path) ->
receive
stop -> stop;
{getPath,From} ->
From ! Path,
distanceServer(Path)
end.
% Searching path
path(From,To,not_found,Paths) -> % if not in the known list, seach for the shortest path
{KM,P} = searchBestPath({0,[From]},To,maps:keys(Paths),{no_dist,no_path}),
case P of
no_path -> not_found;
_ -> {lists:reverse(P),KM}
end;
path(From,To,KM,_) -> % else give the result. Assumption: the known path contains always the best one.
{[From,To],KM}.
searchBestPath({N,[To|_]}=Path,To,_,{BestD,_}) when N < BestD -> Path; % keep the new path if it is better
searchBestPath({N,_},_,_,{BestD,_}=Best) when N >= BestD -> Best; % cancel search if the path so far is longer or equal to the best found
searchBestPath({D,[H|_]=PathSoFar},To,Remaining,Best) ->
Next = [remove(H,{A,B}) || {A,B} <- Remaining, (A =:= H) orelse (B =:= H)], % list of all possible next steps
case Next of
[] -> Best;
Next -> lists:foldl(
fun(X,Acc) ->
{_,ND} = path(H,X), % will always match
R = Remaining -- [orderedTuple(H,X)], % necessary to avoid possible infinite loop in the first search
searchBestPath({D+ND,[X|PathSoFar]},To,R,Acc) % evaluate path for all possible next steps
end,
Best,Next)
end.
% helpers
orderedTuple(A,B) when B > A -> {A,B};
orderedTuple(A,B) -> {B,A}.
remove(X,{X,B}) -> B;
remove(X,{A,X}) -> A.
it uses an external file to define the "known distances", I have used this one for test:
{paris,lyon,465}.
{lyon,marseille,314}.
{marseille,nice,198}.
{marseille,toulouse,404}.
{toulouse,bordeaux,244}.
{bordeaux,paris,568}.
{bordeaux,nantes,347}.
{nantes,paris,385}.
{paris,lille,225}.
{paris,strasbourg,491}.
{lille,strasbourg,525}.
{lille,bruxelles,120}.
{rennes,brest,244}.
{rennes,paris,351}.
{rennes,nantes,113}.
and the result in the shell:
1> c(distances).
{ok,distances}
2> distances:start("distances.txt").
true
3> distances:getTowns().
list of towns :
[bordeaux,brest,bruxelles,lille,lyon,marseille,nantes,nice,paris,rennes,
strasbourg,toulouse]
ok
4> distances:path(bordeaux,bruxelles).
{[bordeaux,paris,lille,bruxelles],913}
5> distances:path(nice,bruxelles).
{[nice,marseille,lyon,paris,lille,bruxelles],1322}
6> distances:path(moscou,paris).
not_found
7> distances:stop().
stop
8>
next step could be to increase the list of known distances each time a new request is done.
The problem occurs in the find function where the erlang shell tells me that an exception error has occurred, it says:
Exception error: no function clause matching seminar1:find("t", []) (seminar1.erl, line 117) in function seminar1:encode/3 ( seminar1.erl, line 113).
I believe what is happening is that the pattern-matching done in the first find function is always failing, though I do not understand why since attempts to do comparisons manually have been successful.
-module(seminar1).
-compile(export_all).
sample() -> "the quick brown fox jumps over the lazy dog
this is a sample text that we will use when we build
up a table we will only handle lower case letters and
no punctuation symbols the frequency will of course not
represent english but it is probably not that far off".
text() -> "this is something that we should encode".
test() ->
Sample = sample(),
Tree = tree(Sample),
Encode = encode_table(Tree),
Decode = decode_table(Tree),
Text = text(),
Seq = encode(Text, Encode),
Text = decode(Seq, Decode).
tree(Sample) -> Freq = freq(Sample),
F = fun({node,N1,V1,_,_}, {node,N2,V2,_,_}) ->
if
V1 > V2 -> false;
V1 == V2 -> if
N1 > N2 -> false;
true -> true
end;
true -> true
end
end,
%lists:sort(F,Freq).
huffman(lists:sort(F,Freq)).
% Calculate the frequency of each letter in the Sample and return a
datastructure of nodes containing the letter involved,
% frequency of it in the sample.
% datastructure {node, Key, Value, Left, Right}
freq(Sample) -> freq(Sample, []).
freq([], Freq) -> Freq;
freq([Char|Rest], Freq) -> freq(Rest, check(Char, Freq)).
% Check function complements the Freq function, it takes the current input
and pattern matches it with the frequency datastructue being built.
% If it scores a hit that particular node has its frequency incremented and
then the whole datastructure is returned.
check(Key, []) -> [{node, [Key], 1, nil, nil}];
check(Key, [{node, [Key], Value, nil, nil}| Tail]) -> [{node, [Key], Value +
1, nil, nil}| Tail];
check(Key, [H|T]) -> [H |check(Key, T)].
% Creates the Huffman tree that is later used to encode a sample.
% The input is the SORTED datastructure derived from the freq-function.
% The leaves of the huffman tree are where actual values reside, branches
are just nodes containing information.
huffman( [ Tree | [] ] ) -> Tree;
huffman([{node, LeftKey, LeftValue, _L1, _R1},
{node, RightKey, RightValue, _L2, _R2} |Tail]) ->
% Creating a branch node
BranchNode = {node, LeftKey ++ RightKey, LeftValue + RightValue, {node,
LeftKey, LeftValue, _L1, _R1}, {node, RightKey, RightValue, _L2, _R2}},
huffman(insert(BranchNode, Tail)).
% A complementary function to the huffman function, inserts the newly made
branchnode into the already sorted tail.
% This is to prevent the sorted tail from becoming unsorted when turning the
tail list into a tree.
% It is inserted as such that the branchnode is the first selection of its
current value,
% meaning that if you have 4 nodes of value 5 ( one being a branchnode) then
the branch node will be the first option.
% This will make the Tree structure left leaning.
%
% N
% N N
% N N
% N N N N
insert(Node, []) -> [Node|[]];
insert(Node, [H|T]) ->
{_, _, Nvalue, _, _} = Node,
{_, _, Hdvalue, _, _} = H,
if
Nvalue =< Hdvalue -> [ Node | [H|T]];
true -> [H | insert(Node, T)]
end.
% Takes the tree created by the huffman-function as input and traverses said
tree.
% Returns a list containing the letters found and their position in the
tree, Left = 0, Right = 1.
% {"e"/[101], [0,0,0]} -- {[Key], [pathway]}
% Traversal method used: Left based traversal.
encode_table(RootNode) -> encode_table(RootNode, [], []).
% When traversing the Tree I need to know the branchnode I am in, the result
list as I am adding letters to it and a PathwayList which is the current
binary path to the branchnode I am in.
encode_table({_, Key, _, nil, nil}, AccList, PathwayList) ->
[AccList | [{Key, reverse(PathwayList)}]];
encode_table({_, _, _, Left, Right}, AccList, PathwayList) ->
encode_table(
Right,
encode_table(Left, AccList, [0| PathwayList]),
[1|PathwayList]).
% Complementary function for the encode_table/3 function, when traversing
the tree the the pathway gets reversed so it needs to be corrected.
reverse(L) -> reverse(L, []).
reverse([], Rev) -> Rev;
reverse([H|T], Rev) -> reverse(T, [H|Rev]).
% Takes a sample text and encodes it in accordance to the encoding table
supplied
encode(Text, Table) -> encode(Text, Table, []).
encode([], _, EncodedText) -> EncodedText;
encode([Letter|Rest], Table, EncodedText) ->
encode(Rest, Table, [find([Letter], Table) | EncodedText]).
% Complementary function to encode/3, searches the Table for the related
Letters binary path.
%find(Letter, []) -> Letter;
find(Letter, [{Letter, BinaryPath} | _Rest]) ->
BinaryPath;
find(Letter, [ _ | Rest]) ->
find(Letter, Rest).
decode_table(tree) -> ok.
decode(sequence, table) -> ok.
test(Letter, [{Letter, Asd} | []]) ->
true;
test(_, _) -> false.
I have tried to follow your code , but I am stuck on the function decode_table(tree) -> ok.. With this spelling, it fails (tree is an atom and won't match anything but tree itself). Change to _Tree to ignore the issue when I understood that the decode functions are not written yet or not provided.
For the encoding, the trouble is that the function encode_table returns a nested list, not suitable with the find function. If you replace the code by encode_table(RootNode) -> lists:flatten(encode_table(RootNode, [], [])). then it works (at least it seems to work since I don't know which result you are expecting)
-module(seminar1).
-compile(export_all).
sample() -> "the quick brown fox jumps over the lazy dog
this is a sample text that we will use when we build
up a table we will only handle lower case letters and
no punctuation symbols the frequency will of course not
represent english but it is probably not that far off".
text() -> "this is something that we should encode".
test() ->
Sample = sample(),
Tree = tree(Sample),
Encode = encode_table(Tree),
%Decode = decode_table(Tree),
Text = text(),
Seq = encode(Text, Encode),
%Text = decode(Seq, Decode).
Seq.
tree(Sample) -> Freq = freq(Sample),
F = fun({node,N1,V1,_,_}, {node,N2,V2,_,_}) ->
if
V1 > V2 -> false;
V1 == V2 -> if
N1 > N2 -> false;
true -> true
end;
true -> true
end
end,
%lists:sort(F,Freq).
huffman(lists:sort(F,Freq)).
% Calculate the frequency of each letter in the Sample and return a
% datastructure of nodes containing the letter involved,
% frequency of it in the sample.
% datastructure {node, Key, Value, Left, Right}
freq(Sample) -> freq(Sample, []).
freq([], Freq) -> Freq;
freq([Char|Rest], Freq) -> freq(Rest, check(Char, Freq)).
% Check function complements the Freq function, it takes the current input
% and pattern matches it with the frequency datastructue being built.
% If it scores a hit that particular node has its frequency incremented and
% then the whole datastructure is returned.
check(Key, []) ->
[{node, [Key], 1, nil, nil}];
check(Key, [{node, [Key], Value, nil, nil}| Tail]) ->
[{node, [Key], Value + 1, nil, nil}| Tail];
check(Key, [H|T]) ->
[H |check(Key, T)].
% Creates the Huffman tree that is later used to encode a sample.
% The input is the SORTED datastructure derived from the freq-function.
% The leaves of the huffman tree are where actual values reside, branches
% are just nodes containing information.
huffman( [ Tree | [] ] ) -> Tree;
huffman([{node, LeftKey, LeftValue, _L1, _R1},
{node, RightKey, RightValue, _L2, _R2} |Tail]) ->
% Creating a branch node
BranchNode = {node, LeftKey ++ RightKey, LeftValue + RightValue, {node, LeftKey, LeftValue, _L1, _R1}, {node, RightKey, RightValue, _L2, _R2}},
huffman(insert(BranchNode, Tail)).
% A complementary function to the huffman function, inserts the newly made
% branchnode into the already sorted tail.
% This is to prevent the sorted tail from becoming unsorted when turning the
% tail list into a tree.
% It is inserted as such that the branchnode is the first selection of its
% current value,
% meaning that if you have 4 nodes of value 5 ( one being a branchnode) then
% the branch node will be the first option.
% This will make the Tree structure left leaning.
%
% N
% N N
% N N
% N N N N
insert(Node, []) -> [Node|[]];
insert(Node, [H|T]) ->
{_, _, Nvalue, _, _} = Node,
{_, _, Hdvalue, _, _} = H,
if
Nvalue =< Hdvalue -> [ Node | [H|T]];
true -> [H | insert(Node, T)]
end.
% Takes the tree created by the huffman-function as input and traverses said tree.
% Returns a list containing the letters found and their position in the
% tree, Left = 0, Right = 1.
% {"e"/[101], [0,0,0]} -- {[Key], [pathway]}
% Traversal method used: Left based traversal.
encode_table(RootNode) -> lists:flatten(encode_table(RootNode, [], [])).
% When traversing the Tree I need to know the branchnode I am in, the result
% list as I am adding letters to it and a PathwayList which is the current
% binary path to the branchnode I am in.
encode_table({_, Key, _, nil, nil}, AccList, PathwayList) ->
[AccList | [{Key, reverse(PathwayList)}]];
encode_table({_, _, _, Left, Right}, AccList, PathwayList) ->
encode_table(
Right,
encode_table(Left, AccList, [0| PathwayList]),
[1|PathwayList]).
% Complementary function for the encode_table/3 function, when traversing
% the tree the the pathway gets reversed so it needs to be corrected.
reverse(L) -> reverse(L, []).
reverse([], Rev) -> Rev;
reverse([H|T], Rev) -> reverse(T, [H|Rev]).
% Takes a sample text and encodes it in accordance to the encoding table supplied
encode(Text, Table) -> encode(Text, Table, []).
encode([], _, EncodedText) -> EncodedText;
encode([Letter|Rest], Table, EncodedText) ->
encode(Rest, Table, [find([Letter], Table) | EncodedText]).
% Complementary function to encode/3, searches the Table for the related
% Letters binary path.
find(Letter, []) -> Letter;
find(Letter, [{Letter, BinaryPath} | _Rest]) ->
BinaryPath;
find(Letter, [ _ | Rest]) ->
find(Letter, Rest).
decode_table(_Tree) -> ok.
decode(sequence, table) -> ok.
test(Letter, [{Letter, _Asd} | []]) ->
true;
test(_, _) -> false.
gives the result:
64> c(seminar1).
{ok,seminar1}
65> rp(seminar1:test()).
[[0,0,0],
[1,0,0,0,1,0],
[0,1,1,1],
[1,0,1,1,0,0],
[0,1,0,0],
[0,0,0],
[1,1,1],
[1,0,0,0,1,0],
[1,0,0,1],
[1,1,0,1,0],
[0,1,1,1],
[1,0,1,0,0],
[0,1,0,1],
[1,1,1],
[0,0,0],
[1,0,1,0,1],
[1,1,1],
[1,1,0,0],
[0,0,1,1],
[1,0,1,0,0],
[1,1,0,0],
[1,1,1],
[1,0,0,0,0,0,0],
[0,1,0,0],
[1,1,0,1,1],
[1,0,1,0,0],
[1,1,0,0],
[0,0,0],
[1,0,0,0,1,1,1],
[0,1,1,1],
[0,1,0,1],
[1,1,1],
[0,1,0,1],
[1,1,0,1,1],
[1,1,1],
[0,1,0,1],
[1,1,0,1,1],
[1,0,1,0,0],
[1,1,0,0]]
ok
66>
Edit
You will get the same result if you replace
encode_table({_, Key, _, nil, nil}, AccList, PathwayList) ->
[AccList | [{Key, reverse(PathwayList)}]];
which is responsible for the nested result by this version which directly produces a flat list.
encode_table({_, Key, _, nil, nil}, AccList, PathwayList) ->
[{Key, reverse(PathwayList)} | AccList];
It is the general way to build a list: [Head|Tail] where Head is any erlang term and Tail is a list. Your code produces a result like [[[],{Key1,Path1}],{Key2,Path2}] while my version gives [{Key2,Path2},{Key1,Path1}]
Some remarks :
In erlang, the usage of if is not frequent, in my opinion mainly because of the last true -> DoSomething() clause which is in most cases very inexpressive.
Another point, searching in a list is not very fast, it is not an issue for isolate search, but in your case the encode and decode functions are doing it for each character, in my opinion, a map is more appropriate to store the Encode and Decode tables than a key/value list.
I am supposed to collect frequencies of characters.
freq(Sample) -> freq(Sample,[]).
freq([],Freq) ->
Freq;
freq([Char|Rest],Freq)->
freq(Rest,[{Char,1}|Freq]).
This function does not work in the right way. If the input is "foo", then the output will be
[{f,1},{o,1},{o,1}].
But I wished to have the output like
[{f,1},{o,2}].
I can't manage to modify element in a tulpe. Can anyone help me out of this and show me how it can be fixed?
a one line solution :o)
% generate a random list
L = [random:uniform(26)+$a-1 || _ <- lists:seq(1,1000)].
% collect frequency
lists:foldl(fun(X,[{[X],I}|Q]) -> [{[X],I+1}|Q] ; (X,Acc) -> [{[X],1}|Acc] end , [], lists:sort(L)).
in action
1> lists:foldl(fun(X,[{[X],I}|Q]) -> [{[X],I+1}|Q] ; (X,Acc) -> [{[X],1}|Acc] end , [], lists:sort("foo")).
[{"o",2},{"f",1}]
quite fast with short list, but the execution time increase a lot with long list (on my PC, it needs 6.5s for a 1 000 000 character text) .
in comparison, with the same 1 000 000 character text Ricardo solution needs 5 sec
I will try another version using ets.
By far the easiest way is to use an orddict to store the value as it already comes with an update_counter function and returns the value in a (sorted) list.
freq(Text) ->
lists:foldl(fun (C, D) -> orddict:update_counter(C, 1, D) end, orddict:new(), Text).
Try with something like this:
freq(Text) ->
CharsDictionary = lists:foldl(fun(Char, Acc) -> dict:update_counter(Char, 1, Acc) end, dict:new(), Text),
dict:fold(fun(Char, Frequency, Acc) -> [{Char, Frequency} | Acc] end, [], CharsDictionary).
The first line creates a dictionary that uses the char as key and the frequency as value (dict:update_counter).
The second line converts the dictionary in the list that you need.
Using pattern matching and proplists.
-module(freq).
-export([char_freq/1]).
-spec char_freq(string()) -> [tuple()].
char_freq(L) -> char_freq(L, []).
char_freq([], PL) -> PL;
char_freq([H|T], PL) ->
case proplists:get_value([H], PL) of
undefined ->
char_freq(T, [{[H],1}|PL]);
N ->
L = proplists:delete([H], PL),
char_freq(T, [{[H],N+1}|L])
end.
Test
1> freq:char_freq("abacabz").
[{"z",1},{"b",2},{"a",3},{"c",1}]
L = [list_to_atom(X) || X <- Str].
D = lists:foldl(fun({Char, _}, Acc) -> dict:update_counter(Char, 1, Acc) end, dict:new(), L).
dict:to_list(D).