文章目录

Erlang 记录:-record() 定义与使用

发布于 2026-04-14 21:24:02 · 浏览 28 次 · 评论 0 条

Erlang 记录(Record)本质上是对元组的一种宏封装。它允许程序员通过字段名而不是索引来访问元组中的数据,从而显著提升代码的可读性和维护性。以下指南将详细介绍如何定义、实例化、操作以及在模块间共享记录。

定义记录

记录的定义通常位于 Erlang 源代码文件的头部,或者专门的头文件(.hrl)中。定义使用 -record 指令。

  1. 打开你的 Erlang 源代码文件(例如 person.erl)。
  2. 输入 -record 指令来声明记录结构。
  3. 指定记录名称和字段列表。字段列表是一个元组,包含原子形式的字段名。你可以设置默认值,语法为 FieldName = DefaultValue。如果不设置默认值,初始值为 undefined
%% 定义一个名为 person 的记录
%% 包含 name, age 和 city 三个字段
%% age 的默认值为 0
-record(person, {name, age = 0, city}).

创建记录实例

定义好记录后,需要在模块中编译或包含它才能使用。创建实例即是构造一个元组,但在语法上使用 # 符号。

  1. 确认记录已在当前模块中定义或通过头文件包含。
  2. 使用 #记录名{} 结构创建一个空实例。未赋值的字段将使用定义时的默认值。
  3. 赋值特定字段。在花括号内输入 字段名 = 值,字段之间用逗号分隔。
%% 创建一个空实例
P1 = #person{}.
%% 结果: {person, undefined, 0, undefined}

%% 创建一个指定 name 的实例
P2 = #person{name = "Alice"}.
%% 结果: {person, "Alice", 0, undefined}

%% 创建一个指定所有字段的实例
P3 = #person{name = "Bob", age = 25, city = "New York"}.

访问与读取字段

在 Erlang 中,读取记录字段最常用的方式是通过模式匹配。虽然可以通过“点语法”读取,但这种方式仅限于编译时已知记录名的情况,且依赖于宏扩展。

  1. 使用模式匹配从记录中提取数据。这是最推荐的方式。
  2. 利用 #记录名.字段名 语法直接获取字段值(仅限字段值是字面量或已知变量时)。
%% 方式一:模式匹配(推荐)
%% 从 P3 中提取 name 和 age
#person{name = Name, age = Age} = P3.
%% Name 变量现在绑定为 "Bob"
%% Age 变量现在绑定为 25

%% 方式二:点语法(直接访问)
%% 获取 P3 的 city 字段
City = P3#person.city.
%% City 变量现在绑定为 "New York"

更新记录

Erlang 的变量是单向绑定的(不可变),因此“更新”记录实际上是创建一个包含新值的新记录副本。更新操作使用原记录作为模板。

  1. 引用原记录变量。
  2. 使用 #记录名{原记录变量 | 字段 = 新值} 语法进行更新。
  3. 指定需要修改的字段。未指定的字段将保留原记录中的值。
%% 原始记录
OldPerson = #person{name = "Tom", age = 20, city = "London"}.

%% 更新 age 为 21,保留其他字段
NewPerson = OldPerson#person{age = 21}.

%% 结果:NewPerson 是 {person, "Tom", 21, "London"}
%% OldPerson 保持不变

在模块间共享记录

为了在多个 Erlang 模块中使用同一个记录定义,通常将记录定义放在单独的头文件中。

  1. 创建一个后缀为 .hrl 的文件(例如 records.hrl)。
  2. 剪切已有的 -record() 定义并粘贴到该头文件中。
  3. 打开需要使用该记录的模块文件(.erl)。
  4. 添加 -include("records.hrl"). 指令。注意文件名需用引号包裹。

records.hrl 内容:

-record(user, {id, username, role = guest}).

app.erl 内容:

-module(app).
-include("records.hrl"). %% 包含记录定义

-export([create_user/1]).

create_user(Name) ->
    %% 现在可以使用 #user{} 了
    #user{username = Name}.

当编译器处理 app.erl 时,它会将 -include 指令替换为 records.hrl 中的具体内容,使得 #user{} 语法生效。


记录与元组的映射

理解记录在底层的存储形式有助于调试。记录在运行时就是普通的元组,元组的第一个元素是记录名(原子)。

  1. 查看记录的内部表示。你可以使用 term_to_string 或直接在 Shell 中打印。

下面的表格展示了记录语法与底层元组的对应关系。

记录语法 底层元组结构 说明
#person{name = "A", age = 1} {person, "A", 1, undefined} 首元素是记录名,后续按定义顺序排列
#person{} {person, undefined, 0, undefined} 使用默认值填充
is_record(T, person) 检查 T 是否是以 person 开头的元组 运行时检查函数

在函数头中使用模式匹配

记录最强大的功能之一是直接在函数定义的头部进行解构和匹配。

  1. 编写函数子句,参数位置直接使用记录模式。
  2. 匹配特定字段值来路由逻辑。
%% 仅处理成年人(age >= 18)的打印
print_info(#person{age = Age} = P) when Age >= 18 ->
    io:format("Adult: ~p~n", [P#person.name]);

%% 处理未成年人
print_info(#person{name = Name}) ->
    io:format("Minor: ~p~n", [Name]).

这种写法避免了在函数体内部写大量的 caseif 语句,代码逻辑更加直观。

graph LR A[输入记录] --> B{模式匹配 age} B -- >= 18 --> C[执行 Adult 分支] B -- < 18 --> D[执行 Minor 分支] C --> E[输出 Adult: Name] D --> F[输出 Minor: Name]

评论 (0)

暂无评论,快来抢沙发吧!

扫一扫,手机查看

扫描上方二维码,在手机上查看本文