因為Elixir是函數式的程式語言,因此function在elixir中是一等公民,可直接宣告存進變數中,也可以做為參數傳入其他函式做執行,基本上跟一般的資料型別是同等的。
函式分成兩種,一種是匿名函式,一種則是有名字的,會在下一章做介紹。
匿名函式
要宣告一個匿名函式,可以使用 fn () -> ... end
的語法。
以下是一個最簡單的例子:
iex(1)> sum = fn (a, b) -> a + b end
#Function<13.126501267/2 in :erl_eval.expr/5>
iex(2)> sum.(1,5)
6
這邊可以注意一件事,就是匿名函式在執行時必須在小括號前加一個點。
然後,配合上一章節所介紹的pattern matching,匿名函式可以做出一些寫法上的變化,例如:
iex(1)> test = fn {a, b} -> [a, b] end
#Function<7.126501267/1 in :erl_eval.expr/5>
iex(2)> test.({1, 2})
[1, 2]
上例的函式會接收一個參數,並用{a, b}與其做模式比對,如同{a, b} = {1, 2}
,最後回傳[1, 2],簡單的完成了把一個tuple轉換成list的功能。
那這邊在改造一下:
iex(1)> test = fn
{a, b} -> [a, b]
[a, b] -> {a, b}
_ -> nil
end
#Function<7.126501267/1 in :erl_eval.expr/5>
iex(2)> test.([1,5])
{1, 5}
iex(3)> test.({1,5})
[1, 5]
iex(4)> test.(1)
nil
可以看到,參數會依次對{a, b}, [a, b], _ 做模式比對,失敗了才會進那個區塊執行並回傳結果。
然後,如果function中不是可以直接一行寫出回傳值的,需用大括號包裹,就如同JS的箭頭函式一般。
iex(1)> test = fn
{a, b} -> {
[a, b]
}
[a, b] -> {
{a, b}
}
_ -> {
nil
}
end
#Function<7.126501267/1 in :erl_eval.expr/5>
當然,對於這個範例來講,上面這個寫法還是過於冗長的。
在可以單行寫出回傳值的情況下,直接用第一種寫法會比較簡短。
閉包特性
匿名函式具有閉包的性質,會記錄下宣告當下的執行環境,且匿名函式跟變數一樣,可以作為函式的回傳值。
依據這兩個性質,可以做到以下這個範例:
iex(1)> say_hi = fn name -> fn -> "Hi " <> name end end
#Function<7.126501267/1 in :erl_eval.expr/5>
iex(2)> f = say_hi.("jack") # 回傳一個匿名函式,且這個函式可以抓到宣告當下(也就是say_hi執行時傳入的name)
#Function<21.126501267/0 in :erl_eval.expr/5>
iex(3)> f.()
"Hi jack"
另一種表示法
當匿名函式可以用一行表達時,可以使用另一種表示法來撰寫。
即以&()包裹住回傳值,再以&1, &2,...替代傳入的參數,例如:
fn x, y, z -> z*(x + y) end
就可以寫成 &(&3*(&1+&2))
而上面的 Hi jack 範例也可以改寫為:
iex(1)> say_hi = &(fn -> ("Hi " <> &1) end)
匿名函式最常見的用法就是配合each map filter等遍例的函式,作為參數傳進去,以進行不同的判斷或是工作。
Enum.map(["jack", "tom", "mark"], fn name -> "Hello #{name}" end)
或是
Enum.map(["jack", "tom", "mark"], &("Hello #{&1}"))