【问题标题】:Erlang pattern matching of a list of records on receive接收记录列表的 Erlang 模式匹配
【发布时间】:2011-11-10 15:25:06
【问题描述】:

我正在尝试为需要在不同时间等待多次更新的应用的一部分创建通用接收。
这是它不工作的原型。

receive_info([])->[];
receive_info([RequiredInfo|RestRequiredInfos]) ->
  receive
    RequiredInfo -> [ RequiredInfo | receie_info(RestRequiredInfos)];
  end.

它被称为

[UserInfo, ContextInfo] = receive_info([#userinfo{},#contextinfo{}]),

所以,我正在尝试发送一个应该匹配的记录类型列表,并且我希望收到一个记录列表。
不确定这是否可行,因为记录将其字段设置为未定义并且模式匹配不起作用,例如:

Trying to receive {user_info,undefined}
Other clause: instead {user_info,12} received

编辑:
由于@Adam Lindberg 提供的is_record 版本对我不起作用,我遵循了同样的做事方式,最终得到:

receive_infos([]) -> [];
receive_infos([Rec|Records]) ->
receive
    %% Guard expression that compares first element in record with supplied record tag
    Record when element(1, Record) =:= Rec -> 
        [ Record | receive_infos(Records)]
end.

【问题讨论】:

    标签: functional-programming erlang pattern-matching


    【解决方案1】:

    几件事。您正在尝试将RequiredInfo(默认记录数据)与RequiredInfo(作为消息发送的填充记录数据)匹配。因此,您只希望获得您已经拥有的数据,我认为这些数据可能不是您想要的。您在 receie_info 上也有一些拼写错误。

    如果我想用收到的填充记录填充默认记录列表,并且必须接收所有记录,否则一切都会失败,我可能会这样做。

    fill_records(List)   -> fill_records(List,length(List)).
    fill_records(List,0) -> List;
    fill_records(List,N) ->
        receive Rec -> fill_records(lists:keyreplace(element(1,Rec),1,List,Rec),N-1)
        after 5000 -> {error,timeout} end.
    

    您应该通过防止接收多个相同记录类型或不需要的记录类型的实例来确保这一点,具体取决于周围的系统。

    您当然也可以只创建一个接收循环,在其中只定义预期的消息数量,或者查看 gen_fsm 或 gen_server 及其内部状态和消息处理。

    【讨论】:

    • 是的,我现在了解到我不能只通过列表和参数传递模式匹配表达式并将它们用于接收模式匹配。现在,您正在做的是将接收到的记录原子与列表中将记录视为元组的记录原子进行比较,不是吗?看起来它应该可以工作,虽然它看起来不是很 erlang-way
    • 一条记录在内部是一个元组,其中记录名称是第一个元素,您不能定义两个名称相同但字段数不同的记录。据我所知,利用这一点并不罕见。 :) 您可以用 is_record 解决方案替换它,但缺点是您必须按顺序获取记录并缓冲任何乱序消息。
    【解决方案2】:

    如果你把它改写成:

    receive_info([]) -> [];
    receive_info([Rec|Records]) ->
        receive
            Msg when is_record(Msg, Rec) ->
                [Msg|receive_info(Records)];
        end.
    

    并将其称为:

    receive_info([userinfo, contextinfo])
    

    它会做你想做的。

    【讨论】:

    • 接收 {Rec,_} 和模式匹配元组标签而不是使用 is_record 不是更好吗?
    • @Arkaitz {Rec,_} 只匹配大小为 2 的元组,这与只有 1 个字段的记录相同。
    • when is_record(Msg,Rec) 是一个非法的守卫表达式。我认为is_record 只能用于带有文字原子的守卫,例如when is_record(Msg,user_info)
    • 我没有对此进行测试,但引用文档:“但是,如果 RecordTag 不是文字原子,则将调用 is_record/2 BIF 并且元组的大小不会已验证。”
    • @Arkaitz:你不应该使用内部记录表示,它被认为是不好的形式,因为当记录改变时它更容易出错。
    【解决方案3】:

    我认为您的最后一个答案是正确的,但您可以再添加一项检查以确保您获得正确大小的记录:

    receive_infos([]) -> [];
    receive_infos([Rec|Records]) ->
    receive
        %% Guard expression that compares first element in record with supplied record tag
        Record when element(1, Record) =:= element(1, Rec), size(Record) =:= size(Rec) -> 
            [ Record | receive_infos(Records)]
    end.
    

    由于大小和第一个元素原子几乎是您从记录中获得的全部,这可能与您将要做的一样好。

    或者更直接地说,接受这两个作为参数:

    receive_infos([]) -> [];
    receive_infos([{Name, Size}|Records]) ->
    receive
        %% Guard expression that compares first element in record with supplied record tag
        Record when element(1, Record) =:= Name, size(Record) =:= Size -> 
            [ Record | receive_infos(Records)]
    end.
    

    并将其称为

    receive_infos([{foo, record_info(size, foo)} | etc. ])
    

    【讨论】:

      猜你喜欢
      • 2013-06-25
      • 2018-01-08
      • 2016-11-19
      • 1970-01-01
      • 2014-06-27
      • 1970-01-01
      • 2011-11-27
      • 2012-06-08
      • 2015-02-09
      相关资源
      最近更新 更多