檢視原始碼 函式
函式宣告語法
一個函式宣告是一連串由分號分隔的函式子句,並以句點 (.
) 結尾。
一個函式子句包含一個子句頭和一個子句體,兩者之間以 ->
分隔。
子句頭包含函式名稱、引數列表,以及一個可選的守衛序列,以關鍵字 when
開始。
Name(Pattern11,...,Pattern1N) [when GuardSeq1] ->
Body1;
...;
Name(PatternK1,...,PatternKN) [when GuardSeqK] ->
BodyK.
函式名稱是一個原子。每個引數都是一個模式。
引數的數量 N
是函式的arity(元數)。函式由模組名稱、函式名稱和元數唯一地定義。也就是說,在同一個模組中,具有相同名稱但元數不同的兩個函式是兩個不同的函式。
在模組 mod
中,名稱為 f
且元數為 N
的函式通常表示為 mod:f/N
。
子句體包含一系列由逗號 (,
) 分隔的表達式。
Expr1,
...,
ExprN
有效的 Erlang 表達式和守衛序列在 表達式 中描述。
範例
fact(N) when N > 0 -> % first clause head
N * fact(N-1); % first clause body
fact(0) -> % second clause head
1. % second clause body
函式求值
當呼叫函式 M:F/N
時,首先會找到該函式的程式碼。如果找不到函式,則會發生 undef
執行期錯誤。請注意,函式必須匯出才能在其定義的模組外部可見。
如果找到該函式,則會依序掃描函式子句,直到找到符合以下兩個條件的子句為止:
- 子句頭中的模式可以成功地與給定的引數匹配。
- 守衛序列(如果有的話)為真。
如果找不到這樣的子句,則會發生 function_clause
執行期錯誤。
如果找到這樣的子句,則會求值對應的子句體。也就是說,會依序求值子句體中的表達式,並傳回最後一個表達式的值。
考慮函式 fact
-module(mod).
-export([fact/1]).
fact(N) when N > 0 ->
N * fact(N - 1);
fact(0) ->
1.
假設您想計算 1 的階乘
1> mod:fact(1).
求值從第一個子句開始。模式 N
與引數 1 匹配。匹配成功,且守衛 (N > 0
) 為真,因此 N
繫結到 1,並求值對應的子句體。
N * fact(N-1) => (N is bound to 1)
1 * fact(0)
現在,呼叫 fact(0)
,並再次依序掃描函式子句。首先,模式 N
與 0 匹配。匹配成功,但守衛 (N > 0
) 為假。其次,模式 0
與引數 0
匹配。匹配成功並求值子句體。
1 * fact(0) =>
1 * 1 =>
1
求值成功,mod:fact(1)
傳回 1。
如果以負數作為引數呼叫 mod:fact/1
,則沒有子句頭匹配。會發生 function_clause
執行期錯誤。
尾遞迴
如果函式體的最後一個表達式是函式呼叫,則會執行尾遞迴呼叫。這樣做是為了確保不消耗系統資源,例如呼叫堆疊。這表示使用尾遞迴呼叫的無限迴圈不會耗盡呼叫堆疊,並且(原則上)可以永遠執行。
範例
loop(N) ->
io:format("~w~n", [N]),
loop(N+1).
先前的階乘範例是一個反例。它不是尾遞迴,因為在遞迴呼叫 fact(N-1)
的結果上進行了乘法運算。
內建函式 (BIFs)
內建函式 (BIFs) 是在執行期系統中以 C 語言程式碼實作的。BIFs 執行在 Erlang 中難以或不可能實作的事情。大多數的 BIFs 屬於模組 erlang
,但也有些 BIFs 屬於其他幾個模組,例如 lists
和 ets
。
最常用的屬於 erlang
的 BIFs 是自動匯入的。它們不需要以模組名稱作為前綴。哪些 BIFs 是自動匯入的在 ERTS 的 erlang
模組中指定。例如,標準類型轉換 BIFs(例如 atom_to_list
)和允許在守衛中使用的 BIFs 可以不用指定模組名稱來呼叫。
範例
1> tuple_size({a,b,c}).
3
2> atom_to_list('Erlang').
"Erlang"