檢視原始碼 匿名函數

匿名函數允許我們儲存和傳遞可執行程式碼,就像整數或字串一樣。讓我們了解更多。

定義匿名函數

Elixir 中的匿名函數由關鍵字 fnend 劃分

iex> add = fn a, b -> a + b end
#Function<12.71889879/2 in :erl_eval.expr/5>
iex> add.(1, 2)
3
iex> is_function(add)
true

在上面的範例中,我們定義了一個接收兩個參數 ab 的匿名函數,並傳回 a + b 的結果。參數總是位於 -> 的左側,而要執行的程式碼則位於右側。匿名函數儲存在變數 add 中。

我們可以透過傳遞參數來呼叫匿名函數。請注意,變數和括號之間需要一個點 (.) 才能呼叫匿名函數。這個點可以清楚說明何時呼叫儲存在變數 add 中的匿名函數,而不是名為 add/2 的函數。例如,如果你有一個儲存在變數 is_atom 中的匿名函數,則 is_atom.(:foo)is_atom(:foo) 之間沒有歧義。如果兩者都使用相同的 is_atom(:foo) 語法,則了解 is_atom(:foo) 實際行為的唯一方法是掃描到目前為止的所有程式碼,以找出 is_atom 變數可能的定義。這種掃描會損害可維護性,因為它要求開發人員在讀寫程式碼時在腦中追蹤額外的內容。

Elixir 中的匿名函數也由它們接收的參數數量來識別。我們可以使用 is_function/2 來檢查函數是否具有任何給定的元數

# check if add is a function that expects exactly 2 arguments
iex> is_function(add, 2)
true
# check if add is a function that expects exactly 1 argument
iex> is_function(add, 1)
false

封閉

匿名函數也可以存取函數定義時範圍內的變數。這通常稱為封閉,因為它們封閉在其範圍內。讓我們定義一個新的匿名函數,它使用我們先前定義的 add 匿名函數

iex> double = fn a -> add.(a, a) end
#Function<6.71889879/1 in :erl_eval.expr/5>
iex> double.(2)
4

函數內部指定的變數不會影響其周圍環境

iex> x = 42
42
iex> (fn -> x = 0 end).()
0
iex> x
42

子句和守衛

類似於 case/2,我們可以在匿名函數的參數上進行模式配對,以及定義多個子句和守衛

iex> f = fn
...>   x, y when x > 0 -> x + y
...>   x, y -> x * y
...> end
#Function<12.71889879/2 in :erl_eval.expr/5>
iex> f.(1, 3)
4
iex> f.(-1, 3)
-3

每個匿名函數子句中的參數數量必須相同,否則會引發錯誤。

iex> f2 = fn
...>   x, y when x > 0 -> x + y
...>   x, y, z -> x * y + z
...> end
** (CompileError) iex:1: cannot mix clauses with different arities in anonymous functions

擷取運算子

在本指南中,我們一直使用符號 name/arity 來指函數。事實上,這個符號實際上可以用來將現有函數擷取到資料類型中,我們可以傳遞它,類似於匿名函數的行為方式。

iex> fun = &is_atom/1
&:erlang.is_atom/1
iex> is_function(fun)
true
iex> fun.(:hello)
true
iex> fun.(123)
false

如您所見,一旦擷取函數,我們就可以將它作為參數傳遞或使用匿名函數符號來呼叫它。上面的傳回值也暗示我們可以擷取在模組中定義的函數

iex> fun = &String.length/1
&String.length/1
iex> fun.("hello")
5

您也可以擷取運算子

iex> add = &+/2
&:erlang.+/2
iex> add.(1, 2)
3

擷取語法也可以用作建立函數的捷徑。當您想要建立主要用於包裝現有函數或運算子的函數時,這很方便

iex> fun = &(&1 + 1)
#Function<6.71889879/1 in :erl_eval.expr/5>
iex> fun.(1)
2

iex> fun2 = &"Good #{&1}"
#Function<6.127694169/1 in :erl_eval.expr/5>
iex> fun2.("morning")
"Good morning"

&1 代表傳遞到函數的第一個參數。上面的 &(&1 + 1)fn x -> x + 1 end 完全相同。您可以在 其文件 中進一步了解擷取運算子 &

接下來,讓我們重新檢視過去學習的一些資料類型,並深入探討它們的工作原理。