Counting in Erlang (how do I increment a variable?

2019-04-07 16:37发布

问题:

I've figured out the Erlang-style loops: tail-recursion with functions that take all the "variables that don't vary":

%% 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.

But, what is the best way to increment a counter in Erlang? In most programming languages, the way you count things is by incrementing a variable (ie. count += 1;). Erlang's variables don't vary, so we have to be creative. Fortunately, we have options...

We can pass a Counter variable with our functions, and increment it with each function call. We can use the process dictionary to store a count, and get and put to increment it. We can use ETS, the local data storage for processes. We can use a counter process (!!!):

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.

I'm sure there are other ways too, depending on the scope. What's considered "best practice" for incrementing a variable in Erlang?

回答1:

Don't use the process dictionary.

The 'normal' loop that you are expecting (ie a for loop or a do while) is usually implemented in a recursive function in Erlang so if you are incrementing a 'normal' counter do it in the function calls like you show up top.

Don't use the process dictionary.

In case you missed, can I just point out that you should not use the process dictionary.



回答2:

It all depends on what you are using the counter for. Anything global like the number of messages handled by q system should use ets:update_counter. If it is not global I usually just include it in the parameters like you showed.



回答3:

I think that you have created a big deal out of it, while you can handle it much more easier.
Consider this implementation of for loop in Erlang :

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

While F is the function that you want to save it's results for values I to Max .
Ain't that to easier to grasp ?



回答4:

The standard way of incrementing a counter is as in your first example. By adding a variable to the call and incrementing it. I think that you get confused by the lack of for loops and possibility to update values.

Note that:

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

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

compiles to (more or less) the same thing as (in pseudo code):

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

If you want to accumulate the result there are ways to do that as well.

Either use the lists package or accumulate the result yourself:

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.

Or something similar based on your example.

Edit: returned Count as part of the return value as well, since it seemed to be important.



标签: erlang