iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 7
1

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
  • 匿名函式參數也是 pattern matching、也可以使用 guards,也可以有多個函式區塊
  • 匿名函式會綁定外部變數
  • 呼叫匿名函式要用 .() ,包括在 pipe 中使用時。

下一篇將介紹 Elixir 函式宣告的最後一部份。Happy hacking! 明天見。


上一篇
Guards 與 Pipe operator
下一篇
函式真正的名字、捕獲運算子及 partial application
系列文
函數式編程: 從 Elixir & Phoenix 入門。31

1 則留言

1
郭佳甯
iT邦新手 5 級 ‧ 2017-12-26 23:41:23

太神啦~

taiansu iT邦新手 5 級‧ 2017-12-27 00:20:21 檢舉

/images/emoticon/emoticon37.gif

我要留言

立即登入留言