了解 Erlang 中的选择性接收

Understanding selective receives in Erlang

我正在阅读有关超时的 LYAE 一章,但我无法理解在以下情况下会发生什么:

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

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

1> c(multiproc).
{ok,multiproc}
2> self() ! {15, high}, self() ! {7, low}, self() ! {1, low}, self() ! {17, high}.      
{17,high}
3> multiproc:important().
[high,high,low,low]

我不明白以下内容:

  1. after 0 就像使用 , 据我所知,如果消息在 receive 子句中匹配,它会发生 EVEN
  2. 阅读第一条消息后,我们有 [15, important()],在第二次迭代时会调用 normal,所以我们有一只手 [15, important() --3-rd call][7,normal() -fourth call]。 那么最终我们如何以某种方式得到两个连接起来的列表。
  3. 阅读前 2 条消息后,我们得到:

important[15]
normal(第一次通话)与 [7]

现在 normal()(第一次呼叫)已经在等待新消息,important 现在将第二次呼叫 normal() 所以现在第二次呼叫不会我们有 [1] ?

我不明白 [7][1] 是如何合并的,因为它们来自对 normal().

的单独调用

我理解 important(),因为结果放在列表的末尾 [Message,important()]。但 normal() 的情况并非如此,因为它被 [=19] 调用=] 并且每次它都应该创建一个新列表。

P.S 我添加了一张图片,进一步解释我的 dilemma.I 我想我现在明白了,最后 2 个分支将 return 他们的结果变成 [7,15] 但我还是不明白顺序是什么。

receive 中,我们将在其中一个匹配子句或 after 子句中 结束。也就是说,after 子句只有在超时内没有匹配的消息时才会被评估。这与 aftertry/catch 中的行为不同,后者将无条件求值。


这两个函数遵循常见的 Erlang 模式:通过递归调用构建列表 - 选择性 receive 在这里并不重要。注意这个表达式:

[Message | important()]

等同于:

[Message] ++ important()

也就是说,我们正在创建一个列表,其第一个元素是我们收到的消息,其尾部由任何递归调用组成 returns。结果是我们得到一个接一个收到的消息列表。事实上,在某些时候我们切换到调用 normal() 而不是 important() 并没有改变这一点 - 我们仍然一次构建列表一个元素。

一步一步:

  1. 我们从 shell
  2. 调用 multiproc:important()
  3. important 收到 {15, high},我们现在正在评估 [high] ++ important()
  4. important 的递归调用忽略了 {7, low} 因为它不匹配,但是接收到 {17, high} 匹配。我们现在正在评估 [high] ++ [high] ++ important()
  5. important 的第二次递归调用没有看到任何匹配的消息并进入 after 子句。我们现在正在评估 [high] ++ [high] ++ normal()
  6. normal 的调用收到 {7, low},我们现在正在评估 [high] ++ [high] ++ [low] ++ normal()
  7. normal 的递归调用收到 {1, low},我们现在正在评估 [high] ++ [high] ++ [low] ++ [low] ++ normal()
  8. normal 的第二次递归调用没有看到任何匹配的消息,并且 returns [] 没有进行递归调用。我们现在有 [high] ++ [high] ++ [low] ++ [low] ++ [],等于 [high, high, low, low].

Saying after 0 its like using , from what i understand, it happens EVEN if the message matches in the receive clause?

错了。如果消息匹配,则跳过 after

After reading the first message we have [15, important()],

错了。 [15 | important()][15, important()] 不同。 15 是 Priority——不是 Message,但它是 Message,例如high,即被插入到列表中。

somehow we end up with two lists that get concatenated.

错了。邮箱中有以下消息:

{15, high} 
{7, low}
{1, low}
{17, high}

第一次重要()调用:

{15, high} matches the important() receive
return value: [high | important()]

现在 erlang 需要调用 important() again 并将 return 值替换为 [high | important()].

邮箱中有以下邮件:

{7, low}
{1, low}
{17, high}

第二次重要()调用:

{7, low} doesn't match the important() receive
{1, low} doesn't match the important() receive
{17, high} matches the important() receive

return value: [high | important()]

代入第一个重要return值的结果:

          [high | important()] -- 2nd important() return value
            |
            V           
[high | important()] --  1st important() return value

给你:

[high | [high | important()] ]

现在,erlang 必须第三次调用 important() 并将第三次调用的 return 值代入该结果。

邮箱中有以下邮件:

{7, low}
{1, low}

第三次重要()调用:

{7, low} doesn't match the important() receive
{1, low} doesn't match the important() receive

So, the after executes immediately--because the timeout is 0.
return value: normal().

将结果 [high | [high | important()] ] 中的 important() 替换为 normal() 得到:

[high | [high | normal()] ]

现在 erlang 必须评估 normal() 并将其 return 值代入该结果。

第一次正常()调用:

 {7, low} matches the normal() receive
 return value:  [low | normal()]

进行替换:

             [low | normal()]
                   |
                   V
[high | [high | normal()] ]

给出:

[high | [high | [low | normal()] ] ]

邮箱中有以下邮件:

{1, low}

第二次正常()调用:

{1, low} matches the normal() receive
return value: [low | normal()]

进行替换:

                        [low | normal()]
                          |
                          V
[high | [high | [low | normal()] ] ]

产生:

[high | [high | [low | [low | normal()] ] ] ]

邮箱中有以下邮件:

 <none>

第 3 次正常调用:

The after executes immediately because the timeout is 0.
return value:  []

替代:

                                []
                                |
                                V
 [high | [high | [low | [low | normal() ] ] ] ]

给出:

 [high | [high | [low | [low | [] ] ] ] ].

这是什么乱七八糟的东西??!

7> [high | [high | [low | [low | [] ] ] ] ].
[high,high,low,low]

在 erlang 中,列表是使用 cons 运算符 | 和嵌套子列表递归定义的。以下是一些其他示例:

8> [1 | [2 | [] ]].
[1,2]

|(cons 运算符)右边的东西必须是一个列表。 Erlang 语法也允许你这样写:

10> [1, 2, 3 | [4, 5]].
[1,2,3,4,5]

再一次,| 运算符右边的东西必须是一个列表,但是你可以在左边写逗号分隔值。