Erlang中的计数(如何递增变量?)

zfciruhq  于 2022-12-08  发布在  Erlang
关注(0)|答案(5)|浏览(125)

我已经弄明白了二郎式的循环:尾递归,其函数采用所有“不变的变量”:

%% 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中增加变量的“最佳实践”是什么?

holgip5t

holgip5t1#

不要使用进程字典。
您所期望的“normal”循环(即for循环或do while)通常在Erlang中的递归函数中实现,因此如果您要递增“normal”计数器,请在函数调用中执行,就像您在顶部显示的那样。
不要使用进程字典。
如果您没有注意到,请允许我指出您不应该使用过程字典。

inn6fuwd

inn6fuwd2#

这完全取决于你使用计数器的目的。任何全局性的东西,比如q system处理的消息数,都应该使用ets:update_counter。如果不是全局性的,我通常就把它包含在参数中,就像你展示的那样。

ssgvzors

ssgvzors3#

考虑以下Erlang中for循环的实现:

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

F是一个函数,可用于保存IMax值的结果。

qf9go6mv

qf9go6mv4#

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

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

如果你想累积结果,也有办法做到这一点。
您可以使用lists软件包或自己累积结果:

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.

或者根据你的例子类似的东西。
编辑:返回Count作为返回值的一部分,因为它似乎很重要。

ftf50wuq

ftf50wuq5#

As of Erlang/OTP 21.2 (released in December 2018), you can use the counters module . The documentation sums it up well:
This module provides a set of functions to do operations towards shared mutable counter variables. The implementation does not utilize any software level locking, which makes it very efficient for concurrent access. The counters are organized into arrays with the following semantics:

  • Counters are 64 bit signed integers.
  • Counters wrap around at overflow and underflow operations.
  • Counters are initialized to zero.
  • Write operations guarantee atomicity. No intermediate results can be seen from a single write operation.
  • Two types of counter arrays can be created with options atomics or write_concurrency . The atomics counters have good allround performance with nice consistent semantics while write_concurrency counters offers even better concurrent write performance at the expense of some potential read inconsistencies. See new/2 .
  • Indexes into counter arrays are one-based. A counter array of size N contains N counters with index from 1 to N.

For example, let's create a counter, increment it by 7, and check the value:

> MyCounterRef = counters:new(1, [atomics]).
{atomics,#Ref<0.3460962489.1601830917.24209>}
> counters:add(MyCounterRef, 1, 7).
ok
> counters:get(MyCounterRef, 1).
7

So where do you store the counter reference, if more than one process needs access to it? You can use persistent_term for that, also added in Erlang/OTP 21.2:

> persistent_term:put(my_counter_ref, MyCounterRef).
ok
> counters:add(persistent_term:get(my_counter_ref), 1, 9).
ok
> counters:get(persistent_term:get(my_counter_ref), 1).
16

Note that persistent_term should only be used for values that seldom or never change. You would presumably create the counter when your application start, store the reference as a persistent term, and then access it while the application is running.

相关问题