iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 8
1
Modern Web

用Elixir學習後端煉金術系列 第 8

Day 8 |> 匿名函式 (Anonymous Function)

因為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}"))

上一篇
Day 7 |> 資料的不變性 (Immutability) 以及 模式比較 (Pattern Matching)
下一篇
Day 9 |> 模組函式 (Named Function)
系列文
用Elixir學習後端煉金術30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言