我正在尝试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
这两种语法有什么区别?
答案 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。