我已经找到了Erlang风格的循环:带有函数的尾递归,这些函数可以获取所有“不变的变量”:
%% 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变量,并在每次函数调用时递增它。我们可以使用流程字典来存储计数,并使用get
和put
来增加计数。我们可以使用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 :(得分:9)
请勿使用流程词典。
您期望的'正常'循环(即for
循环或do while
)通常在Erlang中的递归函数中实现,因此如果您正在递增“正常”计数器,请执行此操作在函数调用中,就像你出现在顶部。
请勿使用流程词典。
如果您错过了,我可以指出您不应该使用流程词典。
答案 1 :(得分:3)
这完全取决于你使用计数器的是什么。任何像q系统处理的消息数量这样的全局应该使用ets:update_counter。如果它不是全局的,我通常只将它包含在你展示的参数中。
答案 2 :(得分:3)
我认为你已经创造了一个大问题,而你可以更轻松地处理它 在Erlang中考虑for循环的这种实现:
for( Max, Max, F ) -> [ F(Max) ];
for( I, Max, F ) -> [ F(I) | for( I+1, Max, F ) ].
虽然F
是要保存的功能,但I
到Max
的结果却是如此。
是不是更容易掌握?
答案 3 :(得分:0)
递增计数器的标准方法与第一个示例相同。通过向调用添加变量并递增它。我认为你因为缺乏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
如果你想积累结果,也有办法做到这一点。
使用list包或自己累积结果:
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作为返回值的一部分,因为它似乎很重要。
答案 4 :(得分:0)
从 Erlang/OTP 21.2(2018 年 12 月发布)开始,您可以使用 the counters
module。文档总结得很好:
这个模块提供了一组函数来对共享的可变计数器变量进行操作。该实现不使用任何软件级锁定,这使得并发访问非常有效。计数器被组织成具有以下语义的数组:
计数器是 64 位有符号整数。
计数器在上溢和下溢操作时环绕。
计数器初始化为零。
写操作保证原子性。单次写操作看不到中间结果。
可以使用选项 atomics
或 write_concurrency
创建两种类型的计数器数组。 atomics
计数器具有良好的全面性能和良好的一致语义,而 write_concurrency
计数器提供了更好的并发写入性能,但代价是一些潜在的读取不一致。请参阅new/2
。
计数器数组的索引是基于 1 的。一个大小为 N 的计数器数组包含 N 个计数器,索引从 1 到 N。
例如,让我们创建一个计数器,将其增加 7,然后检查其值:
> MyCounterRef = counters:new(1, [atomics]).
{atomics,#Ref<0.3460962489.1601830917.24209>}
> counters:add(MyCounterRef, 1, 7).
ok
> counters:get(MyCounterRef, 1).
7
如果有多个进程需要访问它,那么您将计数器引用存储在哪里?您可以为此使用 persistent_term
,这也在 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
请注意,persistent_term
应仅用于很少或永远不会更改的值。您可能会在应用程序启动时创建计数器,将引用存储为持久项,然后在应用程序运行时访问它。