查看源码 记录

记录是一种用于存储固定数量元素的数据结构。它具有命名的字段,类似于 C 语言中的结构体。记录表达式在编译期间会被转换为元组表达式。

更多示例请参见编程示例

定义记录

记录定义由记录的名称和记录的字段名称组成。记录和字段名称必须是原子(atoms)。每个字段都可以给定一个可选的默认值。如果没有提供默认值,则使用 undefined

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

字段的默认值可以是任意表达式,但不能使用任何变量。

记录定义可以放在模块的属性和函数声明之间的任何位置,但定义必须在使用记录之前。

如果一个记录在多个模块中使用,建议将记录定义放在一个包含文件中。

更改

从 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 的值。所有其他字段保留其旧值。

守卫中的记录

由于记录表达式被扩展为元组表达式,因此允许在守卫中创建记录和访问记录字段。但是,所有子表达式(用于初始化字段)也必须是有效的守卫表达式。

示例

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 是元组表示的大小,即比字段数多 1。