檢視原始碼 記錄

記錄是一種用於儲存固定數量元素的資料結構。它具有命名字段,類似於 C 語言中的結構 (struct)。記錄表達式在編譯期間會被轉換為元組表達式。

更多範例請參閱程式設計範例

定義記錄

記錄定義由記錄名稱以及記錄的欄位名稱組成。記錄和欄位名稱必須是原子 (atom)。每個欄位都可以選擇性地設定預設值。如果未提供預設值,則使用 undefined

-record(Name, {Field1 [= Expr1],
               ...
               FieldN [= ExprN]}).

欄位的預設值可以是任意表達式,但不能使用任何變數。

記錄定義可以放置在模組的屬性和函數宣告的任何位置,但定義必須在記錄的任何使用之前。

如果一個記錄在多個模組中使用,建議將記錄定義放在 include 檔案中。

變更

從 Erlang/OTP 26 開始,可以使用本節描述的語法在 Erlang shell 中定義記錄。在較早的版本中,需要使用 shell 內建函數 rd/2

建立記錄

以下表達式會建立一個新的 Name 記錄,其中每個欄位 FieldI 的值為評估相應表達式 ExprI 的值

#Name{Field1=Expr1, ..., FieldK=ExprK}

欄位的順序可以是任意的,不一定與記錄定義中的順序相同,並且可以省略欄位。省略的欄位將會使用它們各自的預設值。

如果多個欄位要被賦予相同的值,可以使用以下結構

#Name{Field1=Expr1, ..., FieldK=ExprK, _=ExprL}

省略的欄位會獲得評估 ExprL 的值,而不是它們的預設值。此功能主要用於建立 ETS 和 Mnesia 匹配函數的模式。

範例

-record(person, {name, phone, address}).

lookup(Name, Tab) ->
    ets:match_object(Tab, #person{name=Name, _='_'}).

存取記錄欄位

Expr#Name.Field

傳回指定欄位的值。Expr 必須評估為 Name 記錄。

範例:

-record(person, {name, phone, address}).

get_person_name(Person) ->
    Person#person.name.

以下表達式傳回指定欄位在記錄的元組表示中的位置

#Name.Field

範例

-record(person, {name, phone, address}).

lookup(Name, List) ->
    lists:keyfind(Name, #person.name, List).

更新記錄

Expr#Name{Field1=Expr1, ..., FieldK=ExprK}

Expr 必須評估為 Name 記錄。將會傳回此記錄的副本,其中每個指定的欄位 FieldI 的值更改為評估相應表達式 ExprI 的值。所有其他欄位都保留它們的舊值。

在 Guard 中使用記錄

由於記錄表達式會擴展為元組表達式,因此允許在 guard 中建立記錄和存取記錄欄位。但是,所有子表達式(用於初始化欄位)也必須是有效的 guard 表達式。

範例

handle(Msg, State) when Msg =:= #msg{to=void, no=3} ->
    ...

handle(Msg, State) when State#state.running =:= true ->
    ...

還有一個型別測試 BIF is_record(Term, RecordTag)

範例

is_person(P) when is_record(P, person) ->
    true;
is_person(_P) ->
    false.

在模式中使用記錄

匹配特定記錄的模式的建立方式與建立記錄的方式相同

#Name{Field1=Expr1, ..., FieldK=ExprK}

在這種情況下,Expr1 ... ExprK 中的一個或多個可以是未綁定的變數。

巢狀記錄

假設有以下記錄定義

-record(nrec0, {name = "nested0"}).
-record(nrec1, {name = "nested1", nrec0=#nrec0{}}).
-record(nrec2, {name = "nested2", nrec1=#nrec1{}}).

N2 = #nrec2{},

存取或更新巢狀記錄可以不用括號來撰寫

"nested0" = N2#nrec2.nrec1#nrec1.nrec0#nrec0.name,
    N0n = N2#nrec2.nrec1#nrec1.nrec0#nrec0{name = "nested0a"},

這等同於

"nested0" = ((N2#nrec2.nrec1)#nrec1.nrec0)#nrec0.name,
N0n = ((N2#nrec2.nrec1)#nrec1.nrec0)#nrec0{name = "nested0a"},

變更

在 Erlang/OTP R14 之前,存取或更新巢狀記錄時必須使用括號。

記錄的內部表示

記錄表達式在編譯期間會被轉換為元組表達式。定義為

-record(Name, {Field1, ..., FieldN}).

的記錄在內部由元組表示

{Name, Value1, ..., ValueN}

在這裡,每個 ValueIFieldI 的預設值。

在編譯期間,會為每個使用記錄的模組新增一個偽函數,以取得有關記錄的資訊

record_info(fields, Record) -> [Field]
record_info(size, Record) -> Size

Size 是元組表示的大小,也就是欄位數加一。