学习Erlang和递归。如何模拟计数器或acumulator?
我对Erlang很陌生,我只学了两三天,很难改变这种模式 在这种情况下,我想做的是一个计数器。实际上,我以为每次调用函数时,它都会增加,但我意识到它的工作原理与可变深度类似 另一方面,我不明白为什么每次通话时深度不会增加 它是如何工作的?我错过什么了吗 < >我在使用for循环期间或在循环期间不尝试使用C++增量变量时遇到了麻烦。 我想学习这门漂亮的函数式语言对我来说有点难 以下是日志结果: 在本练习中,我想做的是简单地计算路径中的所有文件夹和子文件夹。这只是一个练习,但我很难理解和完成它。最重要的是,我的语言一点也不流利学习Erlang和递归。如何模拟计数器或acumulator?,erlang,Erlang,我对Erlang很陌生,我只学了两三天,很难改变这种模式 在这种情况下,我想做的是一个计数器。实际上,我以为每次调用函数时,它都会增加,但我意识到它的工作原理与可变深度类似 另一方面,我不明白为什么每次通话时深度不会增加 它是如何工作的?我错过什么了吗 < >我在使用for循环期间或在循环期间不尝试使用C++增量变量时遇到了麻烦。 我想学习这门漂亮的函数式语言对我来说有点难 以下是日志结果: 在本练习中,我想做的是简单地计算路径中的所有文件夹和子文件夹。这只是一个练习,但我很难理解和完成它。最重
甚至可能有一个函数可以做到这一点,但我想尝试使用我所知甚少的东西来获得它,在Erlang中,我们将处理大量列表数据类型。一个出发点是熟悉Erlang文档中的简明语法。 回到问题上来,文件夹就像一棵树,所以需要沿着从根到所有分支的路径走下去
-module(tut).
-export([folders/1]).
-export([main/0,incr/1]).
main() ->
Folders = folders("C:/Users/David/test"),
LCounter = [0],
pretty_print(Folders, 0, LCounter),
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, LCounter) ->
{CurrrentFolder, ListSubfolders} = Folders,
SignTemp = lists:duplicate(Depth, "-"),
case Depth of
0 -> Sign = SignTemp;
_ -> Sign = "|" ++ SignTemp
end,
io:format("Depth ->~p", [ io_lib:format("~p", [Depth])]),
io:format("| Lcounter ->~p", [LCounter]),
io:format("~s~s~n", [Sign, CurrrentFolder]),
[pretty_print(Subfolder, Depth+1,map(fun tut:incr/1, LCounter))|| Subfolder <- ListSubfolders].
map(_, []) -> [];
map(F, [H|T]) -> [F(H)|map(F,T)].
incr(X) -> X + 1.
如何从主函数调用:
count_folder(Folders, AccumulatorIn) ->
{_CurrentFolder, ListSubfolders} = Folders,
case ListSubfolders of
[] -> %%Base case, i.e. folder has no sub-folders -> return 1 (count of CurrentFolder is always 1) + Accumulator from previous calculation
1 + AccumulatorIn;
_ -> %%Recursive case, i.e. folder has sub-folders -> count for each ListSubfolders recursively
CountListSubfolder = lists:foldl(fun count_folder/2, 0, ListSubfolders),
1 + AccumulatorIn + CountListSubfolder
end.
首先,在Erlang中,我们将处理大量列表数据类型。一个出发点是熟悉Erlang文档中的简明语法。 回到问题上来,文件夹就像一棵树,所以需要沿着从根到所有分支的路径走下去
-module(tut).
-export([folders/1]).
-export([main/0,incr/1]).
main() ->
Folders = folders("C:/Users/David/test"),
LCounter = [0],
pretty_print(Folders, 0, LCounter),
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, LCounter) ->
{CurrrentFolder, ListSubfolders} = Folders,
SignTemp = lists:duplicate(Depth, "-"),
case Depth of
0 -> Sign = SignTemp;
_ -> Sign = "|" ++ SignTemp
end,
io:format("Depth ->~p", [ io_lib:format("~p", [Depth])]),
io:format("| Lcounter ->~p", [LCounter]),
io:format("~s~s~n", [Sign, CurrrentFolder]),
[pretty_print(Subfolder, Depth+1,map(fun tut:incr/1, LCounter))|| Subfolder <- ListSubfolders].
map(_, []) -> [];
map(F, [H|T]) -> [F(H)|map(F,T)].
incr(X) -> X + 1.
如何从主函数调用:
count_folder(Folders, AccumulatorIn) ->
{_CurrentFolder, ListSubfolders} = Folders,
case ListSubfolders of
[] -> %%Base case, i.e. folder has no sub-folders -> return 1 (count of CurrentFolder is always 1) + Accumulator from previous calculation
1 + AccumulatorIn;
_ -> %%Recursive case, i.e. folder has sub-folders -> count for each ListSubfolders recursively
CountListSubfolder = lists:foldl(fun count_folder/2, 0, ListSubfolders),
1 + AccumulatorIn + CountListSubfolder
end.
我添加此答案以完成@Agus one's 在您开始学习Erlang时,熟悉递归是一个好主意。尽管事实上 Erlang的库在许多情况下处理它:列表、地图、gb_树、gen_服务器、gen_状态。。。因此,在许多情况下,它似乎消失了 在一开始,我建议您避免使用这些库,直到您掌握了递归和尾部递归的概念。 然后,正如Agus所说,您应该深入研究众多的Erlang库,它们已经解决了您可能遇到的大多数常见问题 在代码中,您使用的是列表理解。在我看来,这是一种优雅的语法,但我认为它不适合您掌握递归的目标,在您使用它的特定情况下,它隐藏了您真正在做什么:
...
Cnt = count_folder(Folders, 0),
io:format("Count: ~p~n", [Cnt]),
...
在我的答案的最后,我建议你一个代码,如果我使用了一个手工制作的递归
lists:filter(fun(X) -> filelib:is_dir(filename:join(Path,X)) end, DD),
与
最后,与
{Path, lists:map(fun(X) -> folders(filename:join(Path,X)) end , A)},
在我的示例中,我提出了一个不同的手工版本,它还以更标准的树表示形式打印结果
lists:foldl(fun(X,Acc) -> pretty_print(X, Depth+1, Acc + 1) end, Count, ListSubfolders);
它给出:
pretty_print(ListSubfolders,Depth+1,{Count+1, Sign ++ AddSign}).
-module(tut).
-export([main/1]).
main(Path) ->
Folders = folders(Path),
pretty_print(Folders, 0, {1," "},true) - 1.
folders(Path) ->
{ok, DD} = file:list_dir(Path),
Dirs = filterDirs(DD,Path,[]),
{Path,folders(Dirs,Path,[])}.
% I split the pretty print function in 2 parts. The first one takes care of the tuple and prints
% the current folder. It calls the second part to handle the sub-folder list,
% incrementing both the depth and count and evaluates the new "Sign"
% It needs to know if this tuple was the last of the sub-folder list where it belonged
% to adapt the "Sign" value
pretty_print({CurrrentFolder, ListSubfolders}, Depth, {Count,Sign},Last) ->
io:format("Depth -> ~3.B| Count -> ~3.B~s+--~s~n",
[Depth,Count,Sign, filename:basename(CurrrentFolder)]),
AddSign = case Last of
true -> " ";
false -> "| "
end,
pretty_print(ListSubfolders,Depth+1,{Count+1, Sign ++ AddSign}).
% The second part iterates on each sub-folder, it returns the Count value on completion
% and uses it for next element.
pretty_print([H|T],Depth,{Count,Sign}) ->
NewCounter = pretty_print(H,Depth,{Count,Sign},T == []),
pretty_print(T,Depth,{NewCounter,Sign});
pretty_print([],_,{Count,_}) ->
Count.
filterDirs([],_,R) -> lists:reverse(R);
filterDirs([H|T],Path,R) ->
case filelib:is_dir(filename:join(Path,H)) of
true ->
filterDirs(T,Path,[H|R]);
false ->
filterDirs(T,Path,R)
end.
folders([],_,R) -> lists:reverse(R);
folders([H|T],Path,R) -> folders(T,Path,[folders(filename:join(Path,H))|R]).
我添加此答案以完成@Agus one's 在您开始学习Erlang时,熟悉递归是一个好主意。尽管事实上 Erlang的库在许多情况下处理它:列表、地图、gb_树、gen_服务器、gen_状态。。。因此,在许多情况下,它似乎消失了 在一开始,我建议您避免使用这些库,直到您掌握了递归和尾部递归的概念。 然后,正如Agus所说,您应该深入研究众多的Erlang库,它们已经解决了您可能遇到的大多数常见问题 在代码中,您使用的是列表理解。在我看来,这是一种优雅的语法,但我认为它不适合您掌握递归的目标,在您使用它的特定情况下,它隐藏了您真正在做什么:
...
Cnt = count_folder(Folders, 0),
io:format("Count: ~p~n", [Cnt]),
...
在我的答案的最后,我建议你一个代码,如果我使用了一个手工制作的递归
lists:filter(fun(X) -> filelib:is_dir(filename:join(Path,X)) end, DD),
与
最后,与
{Path, lists:map(fun(X) -> folders(filename:join(Path,X)) end , A)},
在我的示例中,我提出了一个不同的手工版本,它还以更标准的树表示形式打印结果
lists:foldl(fun(X,Acc) -> pretty_print(X, Depth+1, Acc + 1) end, Count, ListSubfolders);
它给出:
pretty_print(ListSubfolders,Depth+1,{Count+1, Sign ++ AddSign}).
-module(tut).
-export([main/1]).
main(Path) ->
Folders = folders(Path),
pretty_print(Folders, 0, {1," "},true) - 1.
folders(Path) ->
{ok, DD} = file:list_dir(Path),
Dirs = filterDirs(DD,Path,[]),
{Path,folders(Dirs,Path,[])}.
% I split the pretty print function in 2 parts. The first one takes care of the tuple and prints
% the current folder. It calls the second part to handle the sub-folder list,
% incrementing both the depth and count and evaluates the new "Sign"
% It needs to know if this tuple was the last of the sub-folder list where it belonged
% to adapt the "Sign" value
pretty_print({CurrrentFolder, ListSubfolders}, Depth, {Count,Sign},Last) ->
io:format("Depth -> ~3.B| Count -> ~3.B~s+--~s~n",
[Depth,Count,Sign, filename:basename(CurrrentFolder)]),
AddSign = case Last of
true -> " ";
false -> "| "
end,
pretty_print(ListSubfolders,Depth+1,{Count+1, Sign ++ AddSign}).
% The second part iterates on each sub-folder, it returns the Count value on completion
% and uses it for next element.
pretty_print([H|T],Depth,{Count,Sign}) ->
NewCounter = pretty_print(H,Depth,{Count,Sign},T == []),
pretty_print(T,Depth,{NewCounter,Sign});
pretty_print([],_,{Count,_}) ->
Count.
filterDirs([],_,R) -> lists:reverse(R);
filterDirs([H|T],Path,R) ->
case filelib:is_dir(filename:join(Path,H)) of
true ->
filterDirs(T,Path,[H|R]);
false ->
filterDirs(T,Path,R)
end.
folders([],_,R) -> lists:reverse(R);
folders([H|T],Path,R) -> folders(T,Path,[folders(filename:join(Path,H))|R]).
在列表中递增计数器。使用相同的计数器值调用列表中的每个项。您应该考虑实际的手工递归,或者库函数列表:使用累加器作为额外参数和返回值的FoLDL/3。在列表理解中递增计数器。使用相同的计数器值调用列表中的每个项。您应该考虑一个真正的手工递归,或者库函数列表:使用累加器作为额外参数和返回值的FoLDL/3。非常感谢,非常友好。我将复制文本并创建一个新模块,看看它是如何工作的。尤其是要理解它。事实是我同意你的意见。当我更好地理解递归的工作原理时,我将有时间使用所有这些函数。所以我现在就要尝试一下,我想我需要花很多时间来习惯递归思维,能够始终遵循程序的顺序,以及它的变量的内容
s目前,我已经从一本简单的书开始,然后放弃。。。因为我知道一开始用序列日志编辑我的答案会有点困难。你选择的问题对于第一次接触递归并不容易:你需要使用递归来探索不同级别的目录,你需要再次使用它来探索一个级别的所有目录。经典的阶乘、斐波那契序列或服务器更容易处理。我想我想开始得太快了。所以我会平静地读一本书。我将探索更简单的练习。除此之外,我们还必须补充一点,编程已经被放弃了很长一段时间,当转换为函数式语言时,新的接触变得更加困难。谢谢你所做的一切。我是弗雷德·赫伯特(Fred Herbert)的书或网站的粉丝:当然还有乔·阿姆斯特朗(Joe Armstrong)的Erlang编程。非常感谢,非常感谢。我将复制文本并创建一个新模块,看看它是如何工作的。尤其是要理解它。事实是我同意你的意见。当我更好地理解递归的工作原理时,我将有时间使用所有这些函数。所以我现在就要尝试一下,我想我需要花很多时间来习惯递归思维,能够始终遵循程序的顺序,以及它的变量的内容。目前,我已经从一本简单的书开始,然后放弃。。。因为我知道一开始用序列日志编辑我的答案会有点困难。你选择的问题对于第一次接触递归并不容易:你需要使用递归来探索不同级别的目录,你需要再次使用它来探索一个级别的所有目录。经典的阶乘、斐波那契序列或服务器更容易处理。我想我想开始得太快了。所以我会平静地读一本书。我将探索更简单的练习。除此之外,我们还必须补充一点,编程已经被放弃了很长一段时间,当转换为函数式语言时,新的接触变得更加困难。谢谢你所做的一切。我是弗雷德·赫伯特(Fred Herbert)的书或网站的粉丝:当然还有乔·阿姆斯特朗(Joe Armstrong)的Erlang编程。