檢視原始碼 匿名函數
匿名函數允許我們儲存和傳遞可執行程式碼,就像整數或字串一樣。讓我們了解更多。
定義匿名函數
Elixir 中的匿名函數由關鍵字 fn
和 end
劃分
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
在上面的範例中,我們定義了一個接收兩個參數 a
和 b
的匿名函數,並傳回 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
完全相同。您可以在 其文件 中進一步了解擷取運算子 &
。
接下來,讓我們重新檢視過去學習的一些資料類型,並深入探討它們的工作原理。