Elixir 匿名函式最簡單的宣告語法如下:
foo = fn (x) -> x + 1 end
參數的部份也是個 pattern matching,也能使用 guard 。而參數的括號是可省略的,但一般在兩個參數以上是都會留著括號。
若有多個區塊,則會用多個 ->
,像是這樣:
is_odd = fn
x when is_integer(x) and rem(x, 2) == 1 -> true
_ -> false
end
呼叫時則要用 .()
。這個是跟 Ruby 的 lambda 抄來的 一樣的。
foo.(1)
#=> 2
is_odd.(4)
#=> false
Elixir 刻意讓具名函式與匿名函式的語法有所區隔,因為它們有個很關鍵的行為差異:自由變數綁定。
自由變數,就是呼叫時還未定義在參數或是函式內部的變數。底下範例中的三個 JavaScript 函式,在遇到找不到的變數時,都會去找尋函式外部是否有已定義的同名變數。關於 JavaScript 函式的變數綁定,可以參考 @KuroHsu 這一篇重新認識 JavaScript: Day 10 函式 Functions 的基本概念 裡的說明。
var a = 100;
function foo(b) {
return a + b;
}
var bar = function(b) {
return a + b;
}
var baz = b => a + b;
回到 Elixir ,當具名函式中有未定義的變數,將無法編譯:
defmodule Math do
a = 10
def foo(b) do
a + b
end
end
#=> (CompileError) math.exs:6: undefined function a/0
而匿名函式就可以綁定自由變數:
a = 100
foo = fn b -> a + b end
要注意的是,綁定自由變數時,該變數必須要是當下 scope 內可見的。也就是像這個例子裡的情況是無法通過編譯的:
foo = fn b -> a + b end
a = 100 # 太晚宣告了
若在上次提到的 pipe operator 中有直接 pipe 到匿名函式裡,必須在其結尾加上 .()
,例如:
foo = fn i -> i * 10 end
[1, 2, 3]
|> Enum.max # 具名函式不一定要加括號
|> fn i -> i + 1 end.()
|> foo.()
在高階函式中傳遞匿名函式則不用特別做什麼
Enum.map([1, 2, 3], fn i -> i + 1 end)
fn x -> end
.()
,包括在 pipe 中使用時。下一篇將介紹 Elixir 函式宣告的最後一部份。Happy hacking! 明天見。