2016-06-26 61 views
2

我目前正在Elixir工作。我對Ruby或函數式編程方面的經驗都很少,所以我對語法不太熟悉。我正在閱讀Learn Elixir in Y minutes,其中一個例子讓我有點困惑。起初,指南顯示了控制流結構,我理解得很好。Elixir案例與使用`receive`的類似語法

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 

這兩種語法有什麼區別?

回答

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

此語法與您在case中找到的相似,全部都是長生不老藥。這是在所有允許模式匹配的構造中可以找到的基本模式匹配語法。

其中之一是case,另一個是receive,又一個是fn

  • case允許模式匹配您傳遞給它的變量。
  • receive用於對發送到過程的消息進行模式匹配 - 重要的是receive是有選擇性的。一個receive將只處理一個匹配其中一個模式的消息。所有與任何模式不匹配的消息都將被存儲以備後續處理。如果您在郵箱中留下大量未處理的郵件,這可能很危險 - 掃描郵件與郵箱中的郵件數量成線性關係,太多郵件與任何模式都不匹配,可能需要很長時間才能查看他們。
  • fn定義了一個匿名函數 - 很多人都沒有意識到你可以在它們中匹配模式,並且也定義了多個子句。

例如:

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

除了上面Ryan的答案,按我的理解,接受和情況是藥劑(/二郎)兩個完全不同的事情。

case..end是一個標準塊結構,而receive是一個原子,並在erts/emulator/beam/beam_emu.c中處理,在文件中查找文本「接收語句」。它期望的代碼似乎起源於lib/compiler/src/beam_receive.erl。