在Erlang中计数(如何增加变量?)

在Erlang中计数(如何增加变量?),erlang,Erlang,我已经找到了Erlang风格的循环:带函数的尾部递归,它接受所有“不变的变量”: 但是,在Erlang中增加计数器的最佳方法是什么?在大多数编程语言中,计数的方式是通过增加一个变量(即,count+=1;)。Erlang的变量是不变的,所以我们必须有创造性。幸运的是,我们有选择 我们可以在函数中传递一个计数器变量,并在每次函数调用中递增它。我们可以使用流程字典来存储计数,并使用get和put来增加计数。我们可以使用ETS,即进程的本地数据存储。我们可以使用计数器进程(!!!): 我相信也有其他方

我已经找到了Erlang风格的循环:带函数的尾部递归,它接受所有“不变的变量”:

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

我们可以在函数中传递一个计数器变量,并在每次函数调用中递增它。我们可以使用流程字典来存储计数,并使用
get
put
来增加计数。我们可以使用ETS,即进程的本地数据存储。我们可以使用计数器进程(!!!):


我相信也有其他方法,这取决于范围。在Erlang中递增变量的“最佳实践”是什么?

这完全取决于您使用计数器的目的。任何像q系统处理的消息数量这样的全局性数据都应该使用ets:update\u计数器。如果它不是全局的,我通常只将其包含在您显示的参数中。

不要使用流程字典

您期望的“正常”循环(即
for
循环或
do while
)通常在Erlang中的递归函数中实现,因此,如果要递增“正常”计数器,请在函数调用中执行它,就像显示在顶部一样


不要使用流程字典


如果您没有注意到,我可以指出您不应该使用流程字典。

增加计数器的标准方法与您的第一个示例相同。通过向调用中添加变量并使其递增。我认为,由于缺少for循环和更新值的可能性,您会感到困惑

请注意:

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

repeat(Times, Times) -> done;
repeat(N, Times) ->
  do_a_side_effect,
  repeat(N + 1, Times).
编译为(或多或少)与(在伪代码中)相同的内容:

或者类似的例子


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

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

for( Max, Max, F )  -> [ F(Max) ];
for( I, Max, F )    -> [ F(I) | for( I+1, Max, F ) ].
F
是一个函数,您可以通过该函数将值
I
的结果保存到
Max
自Erlang/OTP 21.2(2018年12月发布)起,您可以使用。文件很好地总结了这一点:

此模块提供一组函数,用于对共享的可变计数器变量执行操作。该实现不使用任何软件级锁定,这使得它对并发访问非常有效。计数器被组织成具有以下语义的数组:

  • 计数器是64位有符号整数

  • 计数器在溢出和下溢操作时环绕

  • 计数器初始化为零

  • 写操作保证原子性。从单个写入操作中看不到中间结果

  • 可以使用选项
    atomics
    write\u concurrency
    创建两种类型的计数器阵列。
    atomics
    计数器具有良好的全面性能和良好的一致性语义,而
    write\u并发
    计数器提供了更好的并发写入性能,但代价是一些潜在的读取不一致性。请参见
    新建/2

  • 计数器数组的索引是基于一个的。大小为N的计数器数组包含索引为1到N的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\u term
应仅用于很少或从未更改的值。您可能会在应用程序启动时创建计数器,将引用存储为永久术语,然后在应用程序运行时访问它。

如果没有用例,这个问题就没有太大意义。您可以执行
lists:foldl
对列表中的内容进行计数(或
筛选
+
长度
)。如果你在计算gen_服务器的调用数,你可以很容易地用服务器的状态来计算。我已经错过了PHP。。。静态$i$i++;另外,不要使用流程字典,但奇怪的是,在Erlang/OTP发行版中,几乎每个应用程序都使用流程字典。像
inets
。或
orber
。或
docbuilder
。或
ic
。或
megaco
。或
tv
。或
cosNotification
。或
eunit
。或
reltool
。或
编译器
。或
erts
。或
测试服务器
。或
appmon
。或者
ssh
。或
调试器
。或
内核
。或
gs
。或
os\u mon
。或
pman
。或
stdlib
。或
percept
。或
xmerl
。或
asn1
。或
mnesia
。或
普通测试
。或
parsetools
。或
透析器
。或如果社区继续关注这条消息,那么更容易相信“无流程字典”的模因。一般规则是“如果你想知道是否应该使用流程字典,你就不应该使用它”和“你会知道什么时候需要它”。公平地说,虽然有有效的流程字典使用,据我所知,它们中的大多数与“递增变量”无关,而是与“存储流程元数据”有关。哦,我同意,我给出了糟糕的建议。我甚至不会考虑使用过程字典来增加变量。我只是觉得“不要使用流程字典”这句无意识的咒语很愚蠢,因为Erlang的核心发行版及其附带的所有lib都广泛使用了它。你的措辞好多了。“如果你想知道答案是否定的”和“你会知道什么时候你需要它”更有意义。
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.
for( Max, Max, F )  -> [ F(Max) ];
for( I, Max, F )    -> [ F(I) | for( I+1, Max, F ) ].
> MyCounterRef = counters:new(1, [atomics]).
{atomics,#Ref<0.3460962489.1601830917.24209>}
> counters:add(MyCounterRef, 1, 7).
ok
> counters:get(MyCounterRef, 1).
7
> 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