《Elixir 101》函式與回傳值

閱讀時間約 11 分鐘
最近又重新開始學 Elixir,這時遇到一個問題:
在 Elixir 裡面要怎麼建立函式,還有函式要怎麼回傳值?
首先這要從 Elixir 有兩種函數類型說起:
  • 匿名函式(Anonymous Functions)
  • 具名函示(Named Functions)

匿名函式 (Anonymous Functions)

匿名函式 (Anonymous Functions),通常指的是這個函式無需有函示的識別符號(identifier)。在有些語言實作上,被稱為 lambda表示式(Pyhton),或者是 閉包(closures) (Rust)。
在 Elixir 裡,匿名函式是宣告一個變數,然後將函式用 fn ... end 包裹起來。
例如我們宣告一個叫 hello_world 的匿名函式,裡面是向顯示器顯示 Hello World ,那我們可以寫作:
hello_world = fn() -> IO.puts "Hello World" end
這時候我們該如何執行這個函式呢?
在其他語言中,可能會很自然的加上 () 作為執行函式的句法,例如上方的 hello_world 變成 hello_world() 來執行。接著就會在 Elixir 中發生錯誤。例如我們在 iex 中試試看:
$ iex
Erlang/OTP 22 [erts-10.5.3] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe] [dtrace]

Interactive Elixir (1.9.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> hello_world = fn() -> IO.puts "Hello World" end
#Function<21.126501267/0 in :erl_eval.expr/5>
iex(2)> hello_world()
** (CompileError) iex:2: undefined function hello_world/0
在 Elixir 中,必須加入 . (dot) 來執行這個匿名函式(. (dot) 在 Elixir 裡面也是一個運算符號,基於本篇主題,留待下次再說明)。所以執行上方範例的 hello_world 就要變成:
hello.()
這時候我們在 iex 在實驗一次,就可以知道成功執行啦!
$ iex
Erlang/OTP 22 [erts-10.5.3] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe] [dtrace]

Interactive Elixir (1.9.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> hello_world = fn() -> IO.puts "Hello World" end
#Function<21.126501267/0 in :erl_eval.expr/5>
iex(2)> hello_world.()
Hello World
:ok
iex(3)>

具名函式(Named Functions)

相對於匿名函式,具名函式具有識別符。不過在 Elixir 裡面宣告一個具名函式,和其他語言一些不同。例如在 JavaScript 我們可以直接用 function 語法宣告一個 helloWorld 的具名函式:
function helloWorld(){
console.log('hello world');
}
但在 Elixir 裡面我們可以這樣做嗎?是不是可以將匿名函式的 fn...end 語法直接拿來宣告為:
fn hello_world = () -> IO.puts "hello world"

答案是不能的。

在 Elixir 裡,具名函式的運作範圍僅限於模組(module)。換言之,必須將函式定義在 module 裡。例如我們定義一個 MyModule 名稱的 module,裡面有一個具名函式稱為 hello_world :
defmodule MyModule do
def hello_world() do
IO.puts "hello world"
end
end
當我們在 module 裡面定義好一個具名函式,這時候我們一樣就可以用 . (dot) ,來呼叫這個 module 裡面特定的具名函式。例如上述案例,我們要呼叫 MyModule 裡面的 hello_world 我們就是:
MyModule.hello_world()
在 iex 裡面實驗一次:
$ iex
Erlang/OTP 22 [erts-10.5.3] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe] [dtrace]

Interactive Elixir (1.9.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> defmodule MyModule do
...(1)> def hello_world do
...(1)> IO.puts "hello world"
...(1)> end
...(1)> end
{:module, MyModule,
<<70, 79, 82, 49, 0, 0, 4, 104, 66, 69, 65, 77, 65, 116, 85, 56, 0, 0, 0, 149,
0, 0, 0, 15, 15, 69, 108, 105, 120, 105, 114, 46, 77, 121, 77, 111, 100, 117,
108, 101, 8, 95, 95, 105, 110, 102, 111, ...>>, {:hello_world, 0}}
iex(2)> MyModule.hello_world()
hello world
:ok
iex(3)>

函式的回傳值

以上介紹了兩種函示:匿名函式以及具名函式。那我們在函式中,要怎麼回傳函式的運算值呢?
在程式語言中,函式回傳值主要有兩種表示方式:
  • 用 return 或相類似的語法
  • 回傳值為函式結構中最後的表示式
第一種方式就例如 JavaScript,在函式結構中用 return 語法回傳函式運算值,如果未設定的話,就是回傳 undefined:
function helloWorld(shouldReturn){
if(shouldReturn){
return 'hello world';
}
}

console.log(helloWorld(true)); // 將輸出 'hello world'
console.log(helloWorld(false)); // 將輸出 undefined
而在 Elixir 裡,則是採用第二種方式,將函式結構中最後表示式作為回傳值。如果沒有,則回傳 nil ,即空值。
以之前 MyModule 為例,我們新增另一個 hello 具名函式,而他的函式結構中未有任何程式碼。並將 hello_world 中的 IO.puts "hello world" 改為 "hello world":
defmodule MyModule do
def hello_world() do
"hello world"
end

def hello() do
end
end
這時候 hello_world 將回傳字串值 hello_world ,而 hello 將僅回傳 nil。我們可以在 iex 中試一下:
$ iex
Erlang/OTP 22 [erts-10.5.3] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe] [dtrace]

Interactive Elixir (1.9.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> defmodule MyModule do
...(1)> def hello_world() do
...(1)> 'hello world'
...(1)> end
...(1)> def hello() do
...(1)> end
...(1)> end
{:module, MyModule,
<<70, 79, 82, 49, 0, 0, 4, 120, 66, 69, 65, 77, 65, 116, 85, 56, 0, 0, 0, 144,
0, 0, 0, 15, 15, 69, 108, 105, 120, 105, 114, 46, 77, 121, 77, 111, 100, 117,
108, 101, 8, 95, 95, 105, 110, 102, 111, ...>>, {:hello, 0}}
iex(2)> MyModule.hello_world()
'hello world'
iex(3)> MyModule.hello()
nil
iex(4)>

參考文獻

1會員
5內容數
身為一個軟體工程師,在現今如此快速變化的世界中,該如何確保自生不被滅亡呢?
留言0
查看全部
發表第一個留言支持創作者!
工法人的沙龍 的其他內容
最近開始接觸 Elixir 這門語言,語法風格接近 Ruby。而他有一種動靜皆備的特性,老實說還蠻吸引我的。
最近開始接觸 Elixir 這門語言,語法風格接近 Ruby。而他有一種動靜皆備的特性,老實說還蠻吸引我的。
你可能也想看
Google News 追蹤
Thumbnail
接下來第二部分我們持續討論美國總統大選如何佈局, 以及選前一週到年底的操作策略建議 分析兩位候選人政策利多/ 利空的板塊和股票
Thumbnail
🤔為什麼團長的能力是死亡筆記本? 🤔為什麼像是死亡筆記本呢? 🤨作者巧思-讓妮翁死亡合理的幾個伏筆
Thumbnail
本文探討Python中函式的定義和作用,重點解釋如何通過函式定義def 來解決重複程式碼的問題,並介紹函式引數的預設值和可變引數的使用。並分析了函式的命名衝突問題,以及函式的作用域管理,特別是全域性和區域性變數的區別。這篇文章將幫助初學者更好地理解Python函式的基礎知識,提升程式碼質量。
Thumbnail
本篇文章深入探討韓國電影《下一個素熙》,該片改編自真實事件,揭示了年輕接線生在職場上的內心苦鬥與挑戰。透過對主人公金素熙的故事呈現,展現了面對惡劣工作環境與壓力的現實,並強調了政府和社會對這些「感情勞動者」應有的關注與支持。電影不僅是藝術表現,也是社會問題的反思。
Thumbnail
展覽中我們似乎可以從作品中看出一些東西、但又不那麼明確,覺得他就是個色彩斑斕的抽象畫,但又依稀在其中看到一些具體的形象。我覺得那就像是記憶畫面在我們心靈裡沉積,並變化為精神、變成情感的一個過程。
Thumbnail
今天的作品是古典花型,強調線條的美感。 器具:日式冠軍杯、海綿、花材剪、水果刀。 花材:紫桔梗、火鶴、白藜麥、多瓣百合(又名玫瑰百合)、巴西木葉、白色金魚草、粉紫雲南菊。
Thumbnail
題目敘述 題目會給我們一張資料表Triangle。 裡面分別有x、 y、z等欄位,分別代表三個邊長。其中(x、 y、z)做為複合主鍵Primary key 要求我們判斷,每條data row裡面的x, y, z 能否構成一個合法的三角形? 如果可以,返回字串"Yes";如果不行,返回字串"No
Thumbnail
宇宙是嚴格的生命教練,又是溫柔地守護母親; 所說的話,祂都有在聽,並提醒著此刻真正要學習的就是靜觀、順流。
Thumbnail
來到桃園之前,一直以為桃園是美食沙漠,但我在這裡生活才一個多禮拜,就已經發現不少街邊美食了,請大家不要再誤會桃園了~~ 1.乾拌-韓式甘甜醬🌶️ 甘甜醬
Thumbnail
接下來第二部分我們持續討論美國總統大選如何佈局, 以及選前一週到年底的操作策略建議 分析兩位候選人政策利多/ 利空的板塊和股票
Thumbnail
🤔為什麼團長的能力是死亡筆記本? 🤔為什麼像是死亡筆記本呢? 🤨作者巧思-讓妮翁死亡合理的幾個伏筆
Thumbnail
本文探討Python中函式的定義和作用,重點解釋如何通過函式定義def 來解決重複程式碼的問題,並介紹函式引數的預設值和可變引數的使用。並分析了函式的命名衝突問題,以及函式的作用域管理,特別是全域性和區域性變數的區別。這篇文章將幫助初學者更好地理解Python函式的基礎知識,提升程式碼質量。
Thumbnail
本篇文章深入探討韓國電影《下一個素熙》,該片改編自真實事件,揭示了年輕接線生在職場上的內心苦鬥與挑戰。透過對主人公金素熙的故事呈現,展現了面對惡劣工作環境與壓力的現實,並強調了政府和社會對這些「感情勞動者」應有的關注與支持。電影不僅是藝術表現,也是社會問題的反思。
Thumbnail
展覽中我們似乎可以從作品中看出一些東西、但又不那麼明確,覺得他就是個色彩斑斕的抽象畫,但又依稀在其中看到一些具體的形象。我覺得那就像是記憶畫面在我們心靈裡沉積,並變化為精神、變成情感的一個過程。
Thumbnail
今天的作品是古典花型,強調線條的美感。 器具:日式冠軍杯、海綿、花材剪、水果刀。 花材:紫桔梗、火鶴、白藜麥、多瓣百合(又名玫瑰百合)、巴西木葉、白色金魚草、粉紫雲南菊。
Thumbnail
題目敘述 題目會給我們一張資料表Triangle。 裡面分別有x、 y、z等欄位,分別代表三個邊長。其中(x、 y、z)做為複合主鍵Primary key 要求我們判斷,每條data row裡面的x, y, z 能否構成一個合法的三角形? 如果可以,返回字串"Yes";如果不行,返回字串"No
Thumbnail
宇宙是嚴格的生命教練,又是溫柔地守護母親; 所說的話,祂都有在聽,並提醒著此刻真正要學習的就是靜觀、順流。
Thumbnail
來到桃園之前,一直以為桃園是美食沙漠,但我在這裡生活才一個多禮拜,就已經發現不少街邊美食了,請大家不要再誤會桃園了~~ 1.乾拌-韓式甘甜醬🌶️ 甘甜醬