在 Elixir 中存储状态
Storing state in Elixir
最近我解决了一个涉及更新大量键值的问题。
当然,我考虑过使用 Map
,并进行 Map.put/3
等操作。
然而,考虑到 Elixir 中数据结构的不可变特性,这似乎还不够:
iex> m = Map.put(%{}, :a, 1)
%{a: 1}
iex> Map.put(m, :b, 2)
%{a: 1, b: 2}
iex> m
%{a: 1}
然后我通过在 GenServer
中保存 Map
的状态并使用 handle_cast/3
调用更新它来解决问题。
一般来说,这是正确的方法吗,还是这里太多了?
你的第一个方法是对的,你只是错了一点。
您应该在更新地图时重新绑定变量,如下所示:
iex> m = Map.put(%{}, :a, 1)
%{a: 1}
iex> m = Map.put(m, :b, 2)
%{a: 1, b: 2}
iex> m
%{a: 1, b: 2}
但你必须明白它不会改变变量,它会创建一个新映射并将其重新绑定到同一个变量。
现在,这种方法是最简单的一种,您必须将此映射传递给使用它的每个函数。作为替代方案,您可以考虑使用 Agent
模块。关于它是什么以及它的用途的所有信息都可以在它的文档中找到。
I then solved the problem by holding the state of the Map
in a GenServer
[...]
Generally, is this the right approach, or was this too much here?
这在很大程度上取决于您的目标。 存储状态的方法有很多种。重新绑定变量,例如:
m = Map.put(%{}, :a, 1)
#⇒ %{a: 1}
m = Map.put(m, :b, 2)
#⇒ %{a: 1, b: 2}
不存储任何东西。它将局部变量 m
绑定到 RHO,一旦控制流离开作用域,这个变量就会被垃圾回收。无论您是否需要在单个范围内使用上述地图,GenServer
(和其他状态持有者)都是大材小用。
OTOH,如果您需要长时间存储状态并在不同范围之间共享(例如,在不同进程之间),GenServer
是实现此目的的最简单方法。在 Elixir 中,我们有 Agent
模块来减少用作简单内存存储的 GenServer
的样板文件,但我的建议是始终使用 GenServer
:迟早 Agent
对您的目的来说会变得太紧。
另外,可以使用 ets
模块来保存内存中的键值存储,在进程之间共享。
dets
是一种存储状态 进程重新启动的方法。
最后,mnesia
是一种 OTP 本机方法,用于在重启和不同节点之间共享状态 (在分布式环境中。)
最近我解决了一个涉及更新大量键值的问题。
当然,我考虑过使用 Map
,并进行 Map.put/3
等操作。
然而,考虑到 Elixir 中数据结构的不可变特性,这似乎还不够:
iex> m = Map.put(%{}, :a, 1)
%{a: 1}
iex> Map.put(m, :b, 2)
%{a: 1, b: 2}
iex> m
%{a: 1}
然后我通过在 GenServer
中保存 Map
的状态并使用 handle_cast/3
调用更新它来解决问题。
一般来说,这是正确的方法吗,还是这里太多了?
你的第一个方法是对的,你只是错了一点。
您应该在更新地图时重新绑定变量,如下所示:
iex> m = Map.put(%{}, :a, 1)
%{a: 1}
iex> m = Map.put(m, :b, 2)
%{a: 1, b: 2}
iex> m
%{a: 1, b: 2}
但你必须明白它不会改变变量,它会创建一个新映射并将其重新绑定到同一个变量。
现在,这种方法是最简单的一种,您必须将此映射传递给使用它的每个函数。作为替代方案,您可以考虑使用 Agent
模块。关于它是什么以及它的用途的所有信息都可以在它的文档中找到。
I then solved the problem by holding the state of the
Map
in aGenServer
[...] Generally, is this the right approach, or was this too much here?
这在很大程度上取决于您的目标。 存储状态的方法有很多种。重新绑定变量,例如:
m = Map.put(%{}, :a, 1)
#⇒ %{a: 1}
m = Map.put(m, :b, 2)
#⇒ %{a: 1, b: 2}
不存储任何东西。它将局部变量 m
绑定到 RHO,一旦控制流离开作用域,这个变量就会被垃圾回收。无论您是否需要在单个范围内使用上述地图,GenServer
(和其他状态持有者)都是大材小用。
OTOH,如果您需要长时间存储状态并在不同范围之间共享(例如,在不同进程之间),GenServer
是实现此目的的最简单方法。在 Elixir 中,我们有 Agent
模块来减少用作简单内存存储的 GenServer
的样板文件,但我的建议是始终使用 GenServer
:迟早 Agent
对您的目的来说会变得太紧。
另外,可以使用 ets
模块来保存内存中的键值存储,在进程之间共享。
dets
是一种存储状态 进程重新启动的方法。
最后,mnesia
是一种 OTP 本机方法,用于在重启和不同节点之间共享状态 (在分布式环境中。)