7

我正在学习 Erlang,并试图弄清楚我如何能够并且应该在进程中保存状态。

例如,我正在尝试编写一个程序,它给出文件中的数字列表,告诉我该文件中是否出现数字。我的方法是使用两个过程

  • 缓存将文件的内容读入一个集合,然后等待数字检查,然后回复它们是否出现在集合中。

    is_member_loop(Data_file) ->
        Numbers = read_numbers(Data_file),
        receive
            {From, Number} ->
                From ! {self(), lists:member(Number, Numbers)},
                is_member_loop(Data_file)
        end.
    
  • 客户端将数字发送到缓存并等待trueorfalse响应。

    check_number(Number) ->
        NumbersPid ! {self(), Number},
        receive
            {NumbersPid, Is_member} ->
                Is_member
        end.
    

这种方法显然是幼稚的,因为每个请求都会读取文件。然而,我在 Erlang 还很陌生,我不清楚在不同请求之间保持状态的首选方式是什么。

我应该使用流程字典吗?对于那种进程状态,我不知道有什么不同的机制吗?

更新

正如user601836所建议的,最明显的解决方案是将一组数字作为参数传递给is_member_loop而不是文件名。这似乎是 Erlang 中的一个常见习语,在精彩的在线书籍Learn you some Erlang中有一个很好的例子。

但是,我认为,对于我想在我的过程中保留的更复杂的状态,这个问题仍然存在。

4

2 回答 2

10

简单的解决方案,您可以将is_member_loop(Data_file)数字列表而不是文件名传递给您的函数。

处理状态的最佳解决方案是使用 gen_server。要了解更多信息,您应该查看记录gen_server 行为也可能有用)。

在实践中:

1) 从基于 gen_server 行为的模块 (yourmodule.erl) 开始 2) 在 gen_server 的 init 函数中读取文件并将其作为状态字段传递:

init([]) ->
    Numbers = read_numbers(Data_file),
{ok, #state{numbers=Numbers}}.

3) 编写一个函数,用于触发对 gen_server 的调用

check_number(Number) ->
    gen_server:call(?MODULE, {check_number, Number}).

4)编写代码以处理从您的函数触发的消息

handle_call({check_number, Number}, _From, #state{numbers=Numbers} = State) ->
    Reply = lists:member(Number, Numbers)},
{reply, Reply, State};

handle_call(_Request, _From, State) ->
    Reply = ok,
{reply, Reply, State}.

5) 从 yourmodule.erl 函数导出check_number

-export([check_number/1]).

关于第 4 点需要解释两点:

a) 我们使用模式匹配提取记录状态中的值

b) 如您所见,我离开了通用句柄调用,否则每当收到与 {check_number, Number} 不同的消息时,您的 gen_server 将由于错误的模式匹配而失败

注意:如果您是 erlang 新手,请不要使用进程字典

于 2012-05-17T12:10:36.183 回答
2

不确定这是多么地道,因为我还不是 Erlang 专业人士,但我会使用ETS来处理这个问题。基本上,

read_numbers_to_ets(DataFile) ->
    Table = ets:new(numbers, [ordered_set]),
    insert_numbers(Table, DataFile),
    Table.

insert_numbers(Table, DataFile) ->
    case read_next_number(DataFile) of
    eof -> ok;
    Num -> ets:insert(numbers, {Num})
    end.

然后你可以定义你is_member

is_member(TableId, Number) ->
    case ets:match(TableId, {Number}) of
    [] -> false; %% no match from ets
    [[]] -> true %% ets found the number you're looking for in that table
    end.

而不是采用 a Data_file,您is_member_loop将采用表的 id 进行查找。

于 2012-05-17T13:59:42.883 回答