Erlang 中的可变数据结构
Mutable data structures in Erlang
我是函数式方法的新手。
对于我的学习,我选择了两种函数式语言,Erlang 和 Clojure。
我正在尝试编写能够存储有关当前连接信息的简单套接字服务器。
例如,如果 socket1 想向 socket2 发送消息,它应该发送到服务器:
"message" 到 socket2。
也可以是JSON。消息格式现在无关紧要。
我使用 Clojure 发现了一些可变数据结构,例如 Agent。在 Clojure 的例子中,我使用 Agent 作为可变数组,我在其中存储所有当前连接。
但是我在 Erlang 中找不到类似的东西。你能帮我解决一下吗?
我用谷歌搜索了我的问题,但我发现 Erlang 中没有可变数据结构。在这种情况下,我可以在哪里以及如何存储所有当前连接?
谢谢。
您可以考虑使用 Erlang Term Storage (ets) table。它是一个内存中的键值存储,允许用新条目替换现有条目。
在深入研究 ets table 之前,值得花时间自己实现类似 ets table 的东西。思路就是你启动一个进程,这个进程的状态就是你存储的数据。
下面是一个存储单元类型进程的简单示例,它可以无限期地保留一个值,也允许用户覆盖这个值。这可以很容易地扩展为在状态中保存字典,允许键+值操作。
-module(simple_storage_cell).
-export([start/0, get/1, put/2]).
start() ->
spawn_link(fun() -> loop([]) end).
get(Pid) ->
Pid ! {get, self()}, %% the self() here gets the pid of the
%% calling process, not the storage cell
receive {retrieved, Val} -> Val
after 500 -> error(timeout)
end.
put(Pid, Val) ->
Pid ! {put, self(), Val}, %% see above, in get/1
receive {saved, Val} -> Val
after 500 -> error(timeout)
end.
loop(StoredItem) ->
receive
{get, RequestingPid} ->
RequestingPid ! {retrieved, StoredItem},
%% we now 'hold' the StoredItem in the cell
loop(StoredItem);
{put, RequestingPid, NewValue} ->
RequestingPid ! {saved, NewValue},
%% we lose the old value, keeping NewValue in the cell
loop(NewValue);
OtherMsg -> error({unexpected_msg, OtherMsg})
end.
这会给您带来以下行为:
1> Cell = simple_storage_cell:start().
<0.33.0>
2> simple_storage_cell:get(Cell).
[]
3> simple_storage_cell:put(Cell, "hello").
"hello"
4> simple_storage_cell:get(Cell).
"hello"
5> simple_storage_cell:get(Cell).
"hello"
6> simple_storage_cell:put(Cell, "new value").
"new value"
7> simple_storage_cell:get(Cell).
"new value"
请注意,这种编程实质上是将全局状态重新引入到(名义上的)无状态系统中。如果您有两个进程 get
ting 和 put
tting 值到一个单元格中,那么您刚刚引入了资源争用并且失去了函数式编程应该给您的许多好处。
如果我可以添加个人注释:选择一种具有严格打字规则的语言作为您的 second
功能语言会更明智。我建议不要学习 Clojure+Erlang,而是像这样的组合:
(Clojure, Scala), (Erlang, Haskell), (Scheme, *ML)
更有可能丰富您的智力工具箱。
我是函数式方法的新手。 对于我的学习,我选择了两种函数式语言,Erlang 和 Clojure。 我正在尝试编写能够存储有关当前连接信息的简单套接字服务器。
例如,如果 socket1 想向 socket2 发送消息,它应该发送到服务器:
"message" 到 socket2。
也可以是JSON。消息格式现在无关紧要。
我使用 Clojure 发现了一些可变数据结构,例如 Agent。在 Clojure 的例子中,我使用 Agent 作为可变数组,我在其中存储所有当前连接。 但是我在 Erlang 中找不到类似的东西。你能帮我解决一下吗?
我用谷歌搜索了我的问题,但我发现 Erlang 中没有可变数据结构。在这种情况下,我可以在哪里以及如何存储所有当前连接?
谢谢。
您可以考虑使用 Erlang Term Storage (ets) table。它是一个内存中的键值存储,允许用新条目替换现有条目。
在深入研究 ets table 之前,值得花时间自己实现类似 ets table 的东西。思路就是你启动一个进程,这个进程的状态就是你存储的数据。
下面是一个存储单元类型进程的简单示例,它可以无限期地保留一个值,也允许用户覆盖这个值。这可以很容易地扩展为在状态中保存字典,允许键+值操作。
-module(simple_storage_cell).
-export([start/0, get/1, put/2]).
start() ->
spawn_link(fun() -> loop([]) end).
get(Pid) ->
Pid ! {get, self()}, %% the self() here gets the pid of the
%% calling process, not the storage cell
receive {retrieved, Val} -> Val
after 500 -> error(timeout)
end.
put(Pid, Val) ->
Pid ! {put, self(), Val}, %% see above, in get/1
receive {saved, Val} -> Val
after 500 -> error(timeout)
end.
loop(StoredItem) ->
receive
{get, RequestingPid} ->
RequestingPid ! {retrieved, StoredItem},
%% we now 'hold' the StoredItem in the cell
loop(StoredItem);
{put, RequestingPid, NewValue} ->
RequestingPid ! {saved, NewValue},
%% we lose the old value, keeping NewValue in the cell
loop(NewValue);
OtherMsg -> error({unexpected_msg, OtherMsg})
end.
这会给您带来以下行为:
1> Cell = simple_storage_cell:start().
<0.33.0>
2> simple_storage_cell:get(Cell).
[]
3> simple_storage_cell:put(Cell, "hello").
"hello"
4> simple_storage_cell:get(Cell).
"hello"
5> simple_storage_cell:get(Cell).
"hello"
6> simple_storage_cell:put(Cell, "new value").
"new value"
7> simple_storage_cell:get(Cell).
"new value"
请注意,这种编程实质上是将全局状态重新引入到(名义上的)无状态系统中。如果您有两个进程 get
ting 和 put
tting 值到一个单元格中,那么您刚刚引入了资源争用并且失去了函数式编程应该给您的许多好处。
如果我可以添加个人注释:选择一种具有严格打字规则的语言作为您的 second
功能语言会更明智。我建议不要学习 Clojure+Erlang,而是像这样的组合:
(Clojure, Scala), (Erlang, Haskell), (Scheme, *ML)
更有可能丰富您的智力工具箱。