Elixir Case vs类似语法使用`receive`

时间:2016-06-26 21:45:44

标签: elixir

我正在尝试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关键字。它看起来像某种匿名案例语法,用于可以使用不同参数的函数。

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

这两种语法有什么区别?

3 个答案:

答案 0 :(得分:4)

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页找到。

答案 1 :(得分:2)

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

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

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

例如:

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

答案 2 :(得分:1)

除了Ryan上面的回答,根据我的理解,接收和案例是Elixir(/ erlang)中两个完全不同的东西。

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