学习Erlang和递归。如何模拟计数器或acumulator?

学习Erlang和递归。如何模拟计数器或acumulator?,erlang,Erlang,我对Erlang很陌生,我只学了两三天,很难改变这种模式 在这种情况下,我想做的是一个计数器。实际上,我以为每次调用函数时,它都会增加,但我意识到它的工作原理与可变深度类似 另一方面,我不明白为什么每次通话时深度不会增加 它是如何工作的?我错过什么了吗 < >我在使用for循环期间或在循环期间不尝试使用C++增量变量时遇到了麻烦。 我想学习这门漂亮的函数式语言对我来说有点难 以下是日志结果: 在本练习中,我想做的是简单地计算路径中的所有文件夹和子文件夹。这只是一个练习,但我很难理解和完成它。最重

我对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编程。