【问题标题】:Elixir Case vs Similar Syntax Using `receive`Elixir 案例与使用 `receive` 的类似语法
【发布时间】:2016-07-02 15:09:25
【问题描述】:

我目前正在尝试使用 Elixir。我对 Ruby 或函数式编程的经验都很少,所以我对语法不太熟悉。我正在阅读Learn Elixir in Y minutes,其中一个示例让我有些困惑。起初,指南显示了case控制流结构,我理解得很好。

case {:one, :two} do
  {:four, :five} ->
    "This won't match"
  {:one, x} ->
    "This will match and bind `x` to `:two` in this clause"
  _ ->
    "This will match any value"
end

但是,最后显示的示例之一是关于从其他进程接收消息的示例。它的语法和结构与case 示例非常相似,但没有使用case 关键字。在我看来,它就像某种匿名 case 语法,用于可以接受不同参数的函数。

defmodule Geometry do
  def area_loop do
    receive do
      {:rectangle, w, h} ->
        IO.puts("Area = #{w * h}")
        area_loop()
      {:circle, r} ->
        IO.puts("Area = #{3.14 * r * r}")
        area_loop()
    end
  end
end

这两种语法有什么区别?

【问题讨论】:

    标签: elixir


    【解决方案1】:

    case 语法接受一个参数,这是进行比较的对象。 receive 不接受参数,而是允许您匹配进程收到的消息。

    查看the receive function 上的文档。

    一个区别是case如果不匹配会报错:

    a = 1
    case a do
      2 ->
        IO.puts("2")
    end
    

    这将显示此异常:

    ** (CaseClauseError) no case clause matching: 1
    

    如果receive 函数收到与其任何子句都不匹配的消息,它不会抱怨。它只会忽略该消息。如果有另一个 receive 块可以处理该消息,则该消息将返回到该进程的邮箱。

    另一个区别是receive 可以在一定时间后超时(如文档中所示):

    receive do
      {:selector, i, value} when is_integer(i) ->
        value
      value when is_atom(value) ->
        value
      _ ->
        IO.puts :stderr, "Unexpected message received"
    after
      5000 ->
        IO.puts :stderr, "No message in 5 seconds"
    end
    

    如果您想确保进程及时收到消息,这会很有帮助。

    此信息也可以在Elixir in Action 书籍的第 165 页上找到。

    【讨论】:

      【解决方案2】:

      这种语法 - 类似于您在case 中可以找到的语法 - 是灵丹妙药。这是您可以在所有允许模式匹配的构造中找到的基本模式匹配语法。

      其中一个是case,另一个是receive,还有一个是fn

      • case 允许对传递给它的变量进行模式匹配。
      • receive 用于对发送到进程的消息进行模式匹配 - 重要的是 receive 是有选择性的。单个receive 将仅处理与其中一种模式匹配的一条消息。所有不匹配任何模式的消息都将被存储以供以后处理。如果您在邮箱中留下大量未处理的邮件,这可能会很危险 - 扫描邮件与邮箱中的邮件数量成线性关系,如果有太多邮件与任何模式都不匹配,可能需要很长时间才能浏览他们。
      • fn 定义了一个匿名函数 - 很多人没有意识到您可以在其中进行模式匹配,并且还可以定义多个子句。

      例如:

      fn
        {:ok, foo}       -> foo
        {:error, reason} -> raise "processing error: #{inspect error}"
      end
      

      【讨论】:

        【解决方案3】:

        除了上面 Ryan 的回答,据我了解,在 Elixir(/erlang) 中,receive 和 case 是两个完全不同的东西。

        case..end 是一个标准的块结构,而 receive 是一个原子,在 erts/emulator/beam/beam_emu.c 中处理,在文件中查找文本“receive statement”。它期望的代码似乎来自 lib/compiler/src/beam_receive.erl。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-11-19
          • 1970-01-01
          • 2013-09-23
          • 2019-03-09
          • 1970-01-01
          相关资源
          最近更新 更多