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