2010-09-29 45 views
5

我已经想通了二郎神式循环:尾递归与把所有的“变量不改变”功能:计数二郎(?我怎么增加一个变量)

%% does something, 80 bytes at a time 
loop(Line, File) -> loop(Line, File, 0). 
loop(Line, File, Count) -> 
    do_something(Line, Count), 
    case file:read(File, 80) of 
     {ok, Line2} -> loop(Line2, File, Count + 1); 
     eof -> file:close(File); 
     {error, Reason} -> {error, Reason} 
    end. 

但是,在Erlang中增加计数器的最佳方法是什么?在大多数编程语言中,计算事物的方式是增加一个变量(即count += 1;)。 Erlang的变量不会变化,所以我们必须具有创造性。幸运的是,我们有选项...

我们可以传递一个Counter变量与我们的函数,并增加它与每个函数调用。我们可以使用进程字典来存储计数,然后使用getput来递增计数。我们可以使用ETS(本地数据存储)来处理流程。我们可以使用一个计数器过程(!!!):

loop(Count) ->        
    receive         
     { incr } -> 
      loop(Count + 1);    
     { report, To } ->      
      To ! { count, Count },    
      loop(Count)       
    end.          

incr(Counter) -> 
    Counter ! { incr }. 

get_count(Counter) ->  
    Counter ! { report, self() }, 
    receive 
     { count, Count } -> Count 
    end. 

我确定还有其他方法,具体取决于范围。在Erlang中增加一个变量被认为是“最佳实践”?

+0

没有用例,这个问题不是太有意义。你可以使用'lists:foldl'来计算列表中的东西(或'filter' +'length')。如果你正在计算'gen_server'调用,你可以很容易地使用你的服务器状态。 – Dustin 2010-09-29 18:39:01

+0

我已经失踪PHP ... static $ i; $ i ++; – Rolf 2011-06-18 17:15:34

回答

3

这一切都取决于你使用的计数器。像q系统处理的全部消息一样,应使用ets:update_counter。如果它不是全局的,我通常只将它包含在你展示的参数中。

9

请勿使用进程字典。

的“正常”的循环,你期待(即for环或do while),所以如果你增加一个“正常”柜台通常在二郎一个递归函数来实现这样做的函数调用像你展示顶部。

请勿使用进程字典。

如果你错过了,我可以指出你不应该使用过程字典。

+3

另外,请勿使用流程字典。 – 2010-09-29 22:43:40

+2

然而奇怪的是,Erlang/OTP分发中几乎每个应用程序都使用了进程字典。像'inets'一样。或'奥伯'。或'docbuilder'。或'ic'。或者'megaco'。或“电视”。或者'cosNotification'。或'eunit'。或'reltool'。或'编译器'。或'erts'。或'test_server'。或'appmon'。或'ssh'。或者'调试器'。或'kernel'。或'gs'。或'os_mon'。或者'pman'。或'stdlib'。或'感知'。或'xmerl'。或'asn1'。或'mnesia'。或'common_test'。或'parsetools'。或'dialyzer'。或者......如果社区留言,那么相信“没有流程辞典”模因会容易得多。 – 2010-10-05 10:54:10

+3

一般规则是“如果您想知道您是否应该使用流程字典,您不应该使用它”,并且“当您需要时,您会知道。”公平地说,虽然有有效的流程字典用法,但据我所知,其中大多数不需要'增加变量',而是'存储流程元数据'。 – 2010-10-05 14:10:33

0

递增计数器的标准方式与第一个示例中的一样。通过在调用中添加一个变量并增加它。我认为你会因缺少循环和更新值的可能性而感到困惑。

需要注意的是:

repeat(Times) when Times >= 0 -> repeat(0, Times). 

repeat(Times, Times) -> done; 
repeat(N, Times) -> 
    do_a_side_effect, 
    repeat(N + 1, Times). 

编译成(或多或少)同样的事情(在伪代码):

repeat(Times) -> 
    while (N < Times) { 
    do_a_side_effect 
    N++ 
    } 
    return done 

如果你希望积累的结果,也有方法可以做到也是如此。

或者使用列表包或自己积累的结果:根据你的榜样

loop(File) -> 
    {ok, Fd} = file:open(File), 
    loop(Fd, 0, []). 

loop(Fd, Count, Acc) -> 
    case file:read(Fd, 80) of 
    {ok, Line} -> 
     Result = do_something(Line, Count), 
     loop(Fd, Count + 1, [Result | Acc]); 
    eof -> 
     file:close(File), 
     {Count, lists:reverse(Acc)}; 
    {error, Reason} -> {error, Reason} 
    end. 

或类似的东西。

编辑:返回计数作为返回值的一部分,因为它似乎很重要。

2

我认为你已经创造了一个很大的交易,而你可以更容易地处理它。
考虑for循环此实现在二郎:

for(Max, Max, F) -> [ F(Max) ]; 
for(I, Max, F) -> [ F(I) | for(I+1, Max, F) ]. 

虽然F是要保存它的结果值IMax功能。
难道不容易把握吗?