檢視原始碼 函式

函式宣告語法

一個函式宣告是一連串由分號分隔的函式子句,並以句點 (.) 結尾。

一個函式子句包含一個子句頭和一個子句體,兩者之間以 -> 分隔。

子句包含函式名稱、引數列表,以及一個可選的守衛序列,以關鍵字 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 執行期錯誤。請注意,函式必須匯出才能在其定義的模組外部可見。

如果找到該函式,則會依序掃描函式子句,直到找到符合以下兩個條件的子句為止:

  1. 子句頭中的模式可以成功地與給定的引數匹配。
  2. 守衛序列(如果有的話)為真。

如果找不到這樣的子句,則會發生 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 屬於其他幾個模組,例如 listsets

最常用的屬於 erlang 的 BIFs 是自動匯入的。它們不需要以模組名稱作為前綴。哪些 BIFs 是自動匯入的在 ERTS 的 erlang 模組中指定。例如,標準類型轉換 BIFs(例如 atom_to_list)和允許在守衛中使用的 BIFs 可以不用指定模組名稱來呼叫。

範例

1> tuple_size({a,b,c}).
3
2> atom_to_list('Erlang').
"Erlang"