【问题标题】:Selective receiving in ErlangErlang 中的选择性接收
【发布时间】:2012-06-13 21:00:15
【问题描述】:

所以我开始学习 Erlang,我对这段代码有点困惑。

 -module(prior).
 -compile(export_all).


    important() ->
      receive
    { Priority, Msg } when Priority > 10 ->
      [Msg | important()]
  after 0 ->
     normal()
  end.

normal() ->
  receive
    { _, Msg } -> 
      [Msg | normal()]
  after 0 ->
      []
  end.

我正在调用代码。

    10> self() ! {15, high}, self() ! {7, low}, self() ! {1, low}, self() ! {17, high}.
    {17,high}
    11> prior:important(). 
        [high,high,low,low]

我知道此代码将首先处理所有高优先级消息,然后是低优先级消息。我对返回值是 [high,high,low,low] 感到困惑,因为我看不到它们在哪里连接在一起。

【问题讨论】:

  • 未连接,consed。连接是当您有两个列表时,L1L2 并将它们连接起来:L1 ++ L2。 Consing 是当你有一个元素 E 和一个列表 L 然后形成扩展列表 [E | L]

标签: erlang erl


【解决方案1】:

它们在这里连接

[Msg | important()]

this important() 是一个函数,所以它有一个返回值,当你在 REPL 中运行它时,它会打印函数的返回值。此值是[Head | Tail] 列表从import() 构建的效果

important() 这是一个常规函数:)

有用吗?

【讨论】:

  • 感谢您的快速回复。因此,即使是从 normal() 调用中收到的低优先级消息也会连接到 [Msg |重要()]子句?
  • 一切。如果您想要大量消息,它也可能会淹没您的 ram,因此您应该将其转换为尾递归函数。我不知道您要构建什么,因为它通常应该是一个 gen_server,其优先级为 que 作为内部数据结构。您正在尝试在进程消息队列中执行此操作,而这件事是有限的,因此一般来说这是个坏主意。
【解决方案2】:

所有 Erlang 函数总是返回一个值。函数important/0 将接收一个高优先级消息,然后在表达式[Msg | important()] 中递归调用自身,该表达式构建一个列表,其中包含最新的Msgimportant/0 将接收的所有其他消息。从important/0 返回的正是这个列表。当没有更多高优先级消息时,important/0 将改为调用normal/0 来读取所有剩余的消息。 normal/0 读取的消息将以与 important/0 相同的方式作为列表返回。这将返回给important/0,然后它将返回到与它返回消息相同的列表中。

请注意,一旦调用了normal/0,就不会再对高优先级消息进行特殊处理,因为再也不会调用important/0。此外,important/0 只会处理队列中已经存在的高优先级消息,因为一旦找不到更多消息,它就会调用normal/0

超时值0的特殊之处在于它会立即超时,但保证首先搜索整个消息队列以查找匹配的消息。

【讨论】:

    【解决方案3】:

    最终的返回值是如何构造的……

    [Msg | important()]第一次返回时,确定最终返回值的形式。唯一需要担心的是,我们还不知道最终返回值的所有细节。因此[Msg | important()] 中的important() 将继续被评估。下面举例说明最终返回值[high,high,low,low]是如何构造的。

    [high | important(                      )]  <---- Defines the final form
            ---------------------------------
            [high | important(             )]   <---- Adds more details
                    ------------------------
                    normal(                )    <---- Adds more details
                    ------------------------
                    [low | normal(        )]    <---- Adds more details
                           ----------------
                           [low | normal()]     <---- Adds more details
                                  --------
                                  [      ]      <---- Adds more details
    ------------------------------------------
    [high | [high | [low | [low | []]]]]
    [high,high,low,low]                         <---- The final return value
    

    代码的工作原理...

    在函数important/0after 0简单的意思是“我不等消息来”——如果我的邮箱里有消息,我会看;如果没有,我会继续(执行normal())而不是在那里等待。邮箱里已经有{15, high}, {7, low}, {1, low}, {17, high}。在 Erlang 中,邮箱中的消息以先到先得的顺序排队。 receive 子句可能很挑剔。它扫描邮箱中的所有邮件并“挑选”它想要的邮件。在我们的例子中,根据{Priority, Msg} when Priority &gt; 10{15, high}{17, high} 首先被选中。 之后,函数normal/0 接管。 {7, low}, {1, low} 按顺序处理(consed)。最后,我们得到了[high,high,low,low]

    显示处理顺序的修改版本...

    我们可以稍微修改一下代码,让处理(consing)的顺序更加明确:

    -module(prior).
    -compile(export_all).
    
    important() ->
        receive
        {Priority, Msg} when Priority > 10 ->
            [{Priority, Msg} | important()] % <---- Edited
        after 0 ->
        normal()
        end.
    
    normal() ->
        receive
        {Priority, Msg} -> % <---- Edited
            [{Priority, Msg} | normal()] % <---- Edited
        after 0 ->
            []
        end.
    

    在 shell 中运行它:

    4> c(prior).
    {ok, prior}
    5> self() ! {15, high}, self() ! {7, low}, self() ! {1, low}, self() ! {17, high}.
    {17,high}
    6> prior:important().
    [{15,high},{17,high},{7,low},{1,low}]
    

    【讨论】:

    • 我编写了 OP 询问的代码(我相信这是我在 Learn You Some Erlang 中的优先事项)并且我同意这个答案。
    猜你喜欢
    • 2017-11-28
    • 2019-12-25
    • 2020-09-11
    • 2012-03-15
    • 1970-01-01
    • 2010-10-31
    • 2010-11-20
    • 2014-04-03
    • 2018-03-15
    相关资源
    最近更新 更多