我正在解决一个问题,我需要记住收到的事件的顺序,但也需要根据事件的 ID 查找事件。如果没有第三方库,我怎样才能在 Erlang 中有效地做到这一点?请注意,我有许多潜在的短暂 Actor ,每个 Actor 都有自己的事件(已经被认为是健忘症,但它需要表格的原子,并且如果我的 Actor 死了,表格就会保留下来)。
-record(event, {id, timestamp, type, data}).
最佳答案
根据 Michael 答案评论中的讨论中包含的详细信息,一个非常简单、可行的方法是在流程状态变量中创建一个元组,该元组将事件的顺序与事件的顺序分开存储。事件的 K-V 存储。
考虑:
%%% Some type definitions so we know exactly what we're dealing with.
-type id() :: term().
-type type() :: atom().
-type data() :: term().
-type ts() :: calendar:datetime().
-type event() :: {id(), ts(), type(), data()}.
-type events() :: dict:dict(id(), {type(), data(), ts()}).
% State record for the process.
% Should include whatever else the process deals with.
-record(s,
{log :: [id()],
events :: event_store()}).
%%% Interface functions we will expose over this module.
-spec lookup(pid(), id()) -> {ok, event()} | error.
lookup(Pid, ID) ->
gen_server:call(Pid, {lookup, ID}).
-spec latest(pid()) -> {ok, event()} | error.
latest(Pid) ->
gen_server:call(Pid, get_latest).
-spec notify(pid(), event()) -> ok.
notify(Pid, Event) ->
gen_server:cast(Pid, {new, Event}).
%%% gen_server handlers
handle_call({lookup, ID}, State#s{events = Events}) ->
Result = find(ID, Events),
{reply, Result, State};
handle_call(get_latest, State#s{log = [Last | _], events = Events}) ->
Result = find(Last, Events),
{reply, Result, State};
% ... and so on...
handle_cast({new, Event}, State) ->
{ok, NewState} = catalog(Event, State),
{noreply, NewState};
% ...
%%% Implementation functions
find(ID, Events) ->
case dict:find(ID, Events) of
{Type, Data, Timestamp} -> {ok, {ID, Timestamp, Type, Data}};
Error -> Error
end.
catalog({ID, Timestamp, Type, Data},
State#s{log = Log, events = Events}) ->
NewEvents = dict:store(ID, {Type, Data, Timestamp}, Events),
NewLog = [ID | Log],
{ok, State#s{log = NewLog, events = NewEvents}}.
这是一个完全简单的实现,并将数据结构的细节隐藏在流程接口(interface)后面。我为什么选择字典?只是因为(这很容易)。如果不更好地了解您的需求,我真的没有理由在 gb_tree 等映射上选择字典。如果您有相对较小的数据(要存储数百或数千个内容),那么这些结构之间的性能通常不会有明显差异。
重要的是,您要清楚地确定该流程应响应哪些消息,然后通过创建公开函数的接口(interface)来强制自己在项目代码的其他地方坚持它 em> 在此模块上。在这后面你可以把字典换成其他东西。如果您确实只需要最新的事件 ID 并且不需要从序列日志中提取第 N 个事件,那么您可以放弃日志,只将最后一个事件的 ID 保留在记录而不是列表中。
因此,首先让像这样非常简单的东西起作用,然后确定它是否确实适合您的需要。如果没有,请调整它。如果现在有效,那就运行它吧——不要过分关注性能或存储(除非你真的被迫这么做)。
如果您稍后发现存在性能问题,请将字典和列表切换为其他内容 - 也许是 gb_tree 或 orddict 或 ETS 或其他内容。关键是现在就让某些东西发挥作用,以便您有一个基础来评估功能并在必要时运行基准测试。 (不过,大多数情况下,我发现无论我开始时指定的原型(prototype)是什么,结果都非常接近最终的解决方案。 )
关于database - 哪种 Erlang 数据结构可用于有序集并且可以进行查找?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34598180/