Erlang/OTP 记录(Records)实战指南:定义、创建、访问与编译期元组展开原理
编程语言语言运行时标准库编译器并发编程【免费下载链接】otpErlang/OTP项目地址https://gitcode.com/gh_mirrors/ot/otp点击查看免费下载Records 是 Erlang/OTP 中用于存储固定数量元素的命名数据结构其作用与 C 语言中的 struct 类似但所有 record 表达式都会在编译期被展开为元组tuple表达式。本文以 Erlang/OTP 参考手册 — Records 为核心系统讲解 record 的定义、创建、字段访问、更新、守卫guard与模式匹配用法并结合本仓库lib/compiler与lib/stdlib的源码深入剖析 record 的元组内部表示与编译器优化路径。读完本文你将能够熟练地在模块与 Shell 中定义和使用 records理解其底层代价并能在 ETS/Mnesia 等场景中借助 record 语法写出高效、可读的匹配代码。Records 是什么命名访问 vs 位置访问Record 是一种“定长、命名字段”的数据结构。与元组相比record 最大的优势在于按名字访问字段而元组只能按位置访问。考虑用元组{Name, Address, Phone}表示一个人若要提取数据只能依赖位置记忆Name element(1, P), Address element(2, P), ...这样的代码难以阅读一旦调整字段顺序或增删字段所有element/2调用都必须逐一核对与修改。而使用 record 后-record(person, {name, phone, address}). Name P#person.name, Address P#person.address, ...内部实现上record 就是一个带标签tag的元组例如{person, Name, Phone, Address}首元素是 record 名原子后面依次是各字段值。更完整的示例可参考 Programming Examples — Records。定义 Records-record属性与默认值Record 定义由 record 名和若干字段名组成record 名与字段名都必须是原子atom。每个字段可以带可选的默认值语法如下-record(Name, {Field1 [ Expr1], ... FieldN [ ExprN]}).要点若字段未提供默认值其默认值即为原子undefined。默认值可以是任意表达式但不能使用任何变量。Record 定义可以放在模块中任意属性与函数声明之间但必须在使用该 record 之前出现。若同一个 record 被多个模块使用推荐把定义放进.hrl头文件再用-include引入。例如-record(person, {name , phone [], address}).这里name默认phone默认[]而address未给默认值因此默认是undefined。变更说明从 Erlang/OTP 26 起可以在 Erlang Shell 中直接使用-record语法定义 record早期版本必须使用m:shell内建函数rd/2。Shell 下维护 record 定义的相关函数详见 STDLIB 的 shell 手册包括rd(RecordName, RecordDefinition)定义 record、rf()/rf(RecordNames)删除定义、rl()打印全部定义、rr(Module)从模块加载定义。需要注意的是每个 Shell job 各自维护一套 record 定义。创建 Records创建 record 的表达式如下其中每个FieldI的值由对应表达式ExprI求值得到#Name{Field1Expr1, ..., FieldKExprK}字段顺序可以任意不必与定义一致字段可以省略省略的字段取各自的默认值。示例 #person{phone[0,8,2,3,4,3,1,2], nameRobert}. #person{name Robert,phone [0,8,2,3,4,3,1,2],address undefined}特殊字段_批量设置所有未指定字段若希望多个字段获得同一个值可使用特殊字段_自 Erlang 5.1/OTP R8B 引入#Name{Field1Expr1, ..., FieldKExprK, _ExprL}此时省略的字段取ExprL的求值结果而非各自的默认值。该特性主要用于构造 ETS 与 Mnesia 的匹配模式-record(person, {name, phone, address}). lookup(Name, Tab) - ets:match_object(Tab, #person{nameName, __}).这里__把未显式指定的phone、address字段都设为原子_它正是ets:match/2、mnesia:match_object/3中的通配符。在 Shell 中演示 #person{name Jakob, _ _}. #person{name Jakob,phone _,address _}访问 Record 字段读取字段值Expr#Name.Field其中Expr求值结果必须是一个Namerecord表达式返回指定字段的值-record(person, {name, phone, address}). get_person_name(Person) - Person#person.name.获取字段在元组中的位置#Name.Field返回指定字段在 record 的元组表示中的位置下标从 1 开始位置 1 是 record 名本身。这个表达式是编译期常量常用于lists:keyfind/3等按位置操作的场景-record(person, {name, phone, address}). lookup(Name, List) - lists:keyfind(Name, #person.name, List).更新 RecordsExpr#Name{Field1Expr1, ..., FieldKExprK}Expr须求值为一个Namerecord。该表达式返回该 record 的一个副本其中指定字段被更新为新值其余字段保持原值 P1 #person{nameJoe, phone[1,2,3], addressA street}. P2 P1#person{nameRobert}. #person{name Robert,phone [1,2,3],address A street}注意P1本身并未被修改——Erlang 数据不可变更新 record 实际是构造新元组这一点由编译器的 copy 语义保证。Records 在 Guards 中的使用由于 record 表达式在编译期会被展开为元组表达式在 guard 中创建 record 与访问其字段是允许的但所有子表达式用于初始化字段的表达式本身也必须是合法的 guard 表达式handle(Msg, State) when Msg : #msg{tovoid, no3} - ... handle(Msg, State) when State#state.running : true - ...此外还有类型测试 BIFis_record(Term, RecordTag)用于在 guard 中判断一个值是否为某类 recordis_person(P) when is_record(P, person) - true; is_person(_P) - false.is_record/2的底层实现是检查“该元组首元素是否为给定原子、且元组大小是否匹配”。在编译器的类型分析中lib/compiler/src/beam_call_types.erl 中的is_record分支甚至会结合 record 定义推导出精确的元组形状从而生成更紧凑的运行时检查。标准库中这类用法非常普遍例如 lib/stdlib/src/dets.erl 大量使用is_record(H2, head)、is_record(NewHead, head)作为函数头 guard。Records 在模式匹配中的使用创建 record 的语法同样可以用于构造匹配模式#Name{Field1Expr1, ..., FieldKExprK}此时Expr1 ... ExprK中允许出现一个或多个未绑定变量。字段在模式中可以按任意顺序书写。典型例子是按名字查找电话find_phone([#person{nameName, phonePhone} | _], Name) - {found, Phone}; find_phone([_| T], Name) - find_phone(T, Name); find_phone([], Name) - not_found.也可以配合绑定 #person{name Name} P3, Name. Joe嵌套 RecordsRecord 的字段值本身可以是另一个 record。先看定义-record(nrec0, {name nested0}). -record(nrec1, {name nested1, nrec0#nrec0{}}). -record(nrec2, {name nested2, nrec1#nrec1{}}). N2 #nrec2{},访问或更新嵌套 record 时可以省略括号写成链式形式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 时必须加括号。R14 起允许无括号的链式写法。内部表示编译期展开为元组Record 表达式在编译期被翻译为元组表达式。定义-record(Name, {Field1, ..., FieldN}).在内部被表示为元组{Name, Value1, ..., ValueN}其中每个ValueI是FieldI的默认值。也就是说-record(person, {name , phone [], address}).编译后对应的元组形状是{person, , [], undefined}。record_info/2 伪函数每个使用 record 的模块在编译期都会被注入一个获取 record 信息的伪函数record_info(fields, Record) - [Field] record_info(size, Record) - Sizerecord_info(fields, Record)返回字段名列表record_info(size, Record)返回元组表示的大小即字段数 1多出的 1 是 record 名所在位置。record_info/2的参数必须是编译期可知的字面量见 lib/compiler/doc/notes.md 中“record_info/2is a pseudo-function that requires literal arguments known at compile time”的说明。它在序列化、调试等代码中非常实用例如 lib/compiler/test/compile_SUITE_data/ssh.hrl 中就用record_info(fields, N)配合调试回调遍历 record 的所有字段。编译器视角从 record 语法到 BEAM 指令在本仓库的编译器中可以观察到 record 展开的完整踪迹lib/compiler/src/beam_asm.erl 定义了get_record_field的 BIF 类型bif_type(get_record_field, 3) - {op,get_record_field}并在汇编阶段把 record 定义编码进 BEAM 文件的native_record块build_record_chunk/2、build_record_def/2lib/compiler/src/beam_core_to_ssa.erl 将is_record、is_native_record、get_record_field等 record 相关操作转换为 SSA 形式的中间指令internal_cg分支中对is_record的处理会先取元组大小再与期望值比较lib/compiler/src/beam_block.erl 中的opt_records/1与simplify_get_record_elements_id/5会对get_record_elements_id指令做块级优化例如当只需要提取单个字段时避免为整条 record 复制多个元素。由此可以推断record 语法几乎不带来运行时开销——字段访问最终被优化为按位置读取元组元素record 名字与字段位置在编译期就已解析完毕。这也是为什么标准库源码如 lib/stdlib/src/dets.erl、lib/stdlib/src/array.erl大量使用 record 来表达内部状态结构。实战综合示例结合以上语法给出一个完整的模块示例摘自 Programming Examples — Records。首先是把 record 定义放入头文件供多模块共享%% File: person.hrl %% Data Type: person %% where: %% name: A string (default is undefined). %% age: An integer (default is undefined). %% phone: A list of integers (default is []). %% dict: A dictionary containing various information %% about the person. %% A {Key, Value} list (default is the empty list). -record(person, {name, age, phone [], dict []}).然后是使用该 record 的模块涵盖创建、模式匹配、类型测试、字段访问与更新-module(person). -include(person.hrl). -compile(export_all). % For test purposes only. %% 创建实例。phone 未提供使用默认值 []。 make_hacker_without_phone(Name, Age) - #person{name Name, age Age, dict [{computer_knowledge, excellent}, {drinks, coke}]}. %% 在参数中进行 record 模式匹配 print(#person{name Name, age Age, phone Phone, dict Dict}) - io:format(Name: ~s, Age: ~w, Phone: ~w ~n Dictionary: ~w.~n, [Name, Age, Phone, Dict]). %% 类型测试 字段访问 更新 birthday(P) when is_record(P, person) - P#person{age P#person.age 1}. register_two_hackers() - Hacker1 make_hacker_without_phone(Joe, 29), OldHacker birthday(Hacker1), central_register_server ! {register_person, Hacker1}, central_register_server ! {register_person, OldHacker#person{name Robert, phone [0,8,3,2,4,5,3,1]}}.这段代码展示了 records 的全部核心能力#person{...}创建、print/1参数模式匹配、is_record(P, person)守卫类型测试、P#person.age字段访问以及P#person{age ...}更新。小结与最佳实践优先使用 record 而非裸元组按名字访问字段可读性强字段增删改时无需逐个修正element/2下标。默认值不给默认值的字段为undefined默认值表达式不能使用变量。多模块共享定义将-record放入.hrl头文件并用-include引入。定义须在使用之前无论模块内还是 Shell 中OTP 26 起 Shell 支持-record语法早期版本用rd/2。_特殊字段为 ETS/Mnesia 匹配构造“其余字段全为_通配”的模式。编译期零运行时开销record 在编译期展开为元组record_info(fields|size, Record)提供字段名与大小信息字段访问会被编译器优化为按位置读取可参考 beam_asm.erl、beam_core_to_ssa.erl、beam_block.erl 的实现。嵌套链式访问OTP R14 起无需括号匹配/守卫场景中is_record/2是标准类型测试手段。若需要更多用法示例含 Shell 交互演示与 ETS 匹配模式的完整代码可继续阅读 Programming Examples — Records 与 STDLIB shell 手册。赞分享编程语言语言运行时标准库编译器并发编程【免费下载链接】otpErlang/OTP项目地址https://gitcode.com/gh_mirrors/ot/otp点击查看免费下载相关推荐GKE 生产化就绪审查指南gke-productionize 技能的多领域编排评估与 RAG 评分实战GKE 生产化就绪审查指南gke productionize 技能的多领域编排评估与 RAG 评分实战 导读 gke productionize 是本仓库 s编程语言语言运行时标准库编译器并发编程在 Android 与 Raspberry Pi 上交叉编译 Erlang/OTP完整实战指南在 Android 与 Raspberry Pi 上交叉编译 Erlang/OTP完整实战指南 本指南基于 Erlang/OTP 官方仓库的 HOWTO/IN编程语言语言运行时标准库编译器并发编程Erlang/OTP 交叉编译完全指南基于 otp_build 与 configure 的跨平台构建实战Erlang/OTP 交叉编译完全指南基于 otp_build 与 configure 的跨平台构建实战 本篇指南以 Erlang/OTP 官方交叉编译文档编程语言语言运行时标准库编译器并发编程上一篇实战指南基于OpenVINO的Audacity AI插件开发与部署下一篇Expanding Collection实战5个高级定制技巧让你的iOS应用脱颖而出创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

Delphi连接InterBase/Firebird的IBDAC v9.0.0实战与避坑指南

Delphi连接InterBase/Firebird的IBDAC v9.0.0实战与避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/25 4:47:41 阅读更多 →
Xonsh 编辑器集成完全指南:Sublime Text、VS Code、JetBrains、Emacs、Vim 与内置代码格式化

Xonsh 编辑器集成完全指南:Sublime Text、VS Code、JetBrains、Emacs、Vim 与内置代码格式化

开发工具 【免费下载链接】xonsh 🐚 Python-powered shell. Full-featured, cross-platform and AI-friendly. 项目地址: https://gitcode.com/gh_mirrors/xo/xonsh 点击查看 免费下载 本指南以 docs/editors.rst 为核心,系统梳理 xonsh&…

2026/9/25 4:47:41 阅读更多 →
ESPnet 实战:基于 BEATs 编码器在 ESC-50 上训练音频分类任务的完整 Recipe 解析

ESPnet 实战:基于 BEATs 编码器在 ESC-50 上训练音频分类任务的完整 Recipe 解析

人工智能语音音频深度学习NLP 【免费下载链接】espnet End-to-End Speech Processing Toolkit 项目地址: https://gitcode.com/gh_mirrors/es/espnet 点击查看 免费下载 导读 本文以 egs2/esc50/asr1/README.md 为核心骨架,系统讲解如何在 ESPnet 中以…

2026/9/25 4:47:41 阅读更多 →

最新新闻

The Concise TypeScript Book 精讲:TypeScript 三斜线指令(Triple-Slash Directives)完整指南

The Concise TypeScript Book 精讲:TypeScript 三斜线指令(Triple-Slash Directives)完整指南

文档教程 【免费下载链接】typescript-book The Concise TypeScript Book: A Concise Guide to Effective Development in TypeScript. Free and Open Source. 项目地址: https://gitcode.com/gh_mirrors/typ/typescript-book 点击查看 免费下载 三斜线指令&#x…

2026/9/25 7:16:42 阅读更多 →
Atlas 300V 24G推理加速卡部署YOLO全流程实战:从环境搭建到性能优化

Atlas 300V 24G推理加速卡部署YOLO全流程实战:从环境搭建到性能优化

最近好几个朋友问我一件事:Atlas 300V 24G到底算不算运算加速卡,能不能拿来部署YOLO?问的人多了,我觉得值得专门写一篇来说清楚。这个困惑我也经历过——Atlas家族的产品线实在太杂了,300I、300V、500、800、310、310P…

2026/9/25 7:16:42 阅读更多 →
VoltAgent 接入 Z.AI Coding Plan:模型路由配置、环境变量与源码级解析

VoltAgent 接入 Z.AI Coding Plan:模型路由配置、环境变量与源码级解析

人工智能AI AgentAgent 框架后端多智能体RAG工具调用Agent 记忆 【免费下载链接】voltagent AI Agent Engineering Platform built on an Open Source TypeScript AI Agent Framework 项目地址: https://gitcode.com/gh_mirrors/vo/voltagent 点击查看 免费下载 Z.…

2026/9/25 7:16:42 阅读更多 →
pylibcudf RegexFlags 深入指南:cuDF 字符串正则标志枚举的用法、组合与底层实现

pylibcudf RegexFlags 深入指南:cuDF 字符串正则标志枚举的用法、组合与底层实现

数据分析数据工程机器学习 【免费下载链接】cudf cuDF - GPU DataFrame Library 项目地址: https://gitcode.com/gh_mirrors/cu/cudf 点击查看 免费下载 导读 RegexFlags 是 pylibcudf 字符串子系统中用于控制正则表达式解析行为的标志枚举,是 pylibc…

2026/9/25 7:16:41 阅读更多 →
OpenShell Kubernetes 计算驱动深入解析:Sandbox 生命周期、命名空间模式与驱动配置实战

OpenShell Kubernetes 计算驱动深入解析:Sandbox 生命周期、命名空间模式与驱动配置实战

【免费下载链接】OpenShell OpenShell is the safe, private runtime for autonomous AI agents. 项目地址: https://gitcode.com/gh_mirrors/op/OpenShell 点击查看 免费下载 OpenShell 的 Kubernetes 计算驱动(openshell-driver-kubernetes&#xff0…

2026/9/25 7:16:41 阅读更多 →
VisiData Loader 开发指南:从 open_<filetype> 到 Saver 的完整实战教程

VisiData Loader 开发指南:从 open_<filetype> 到 Saver 的完整实战教程

数据分析CLI数据可视化 【免费下载链接】visidata A terminal spreadsheet multitool for discovering and arranging data 项目地址: https://gitcode.com/gh_mirrors/vi/visidata 点击查看 免费下载 本指南以 VisiData 官方 API 文档(docs/api/loader…

2026/9/25 7:15:41 阅读更多 →

日新闻

AI元人文:从工具使用到思维重构的深度探索

AI元人文:从工具使用到思维重构的深度探索

最近半年我一直在琢磨一件事:AI元人文到底是什么?说白了,就是“用元视角重新审视人与AI的关系”,也在“探索AI如何反向逼着我们发现自己的思考边界”。标题里的“元探索”,在我看就是一层套一层的追问——当你用AI解决…

2026/9/25 0:00:41 阅读更多 →
Python+CNN车牌识别实战:从数据预处理到模型训练与部署

Python+CNN车牌识别实战:从数据预处理到模型训练与部署

简介:基于Python与卷积神经网络的车牌识别项目,面向计算机视觉初学者及智能交通开发者,目标是帮助用户掌握从数据预处理、模型构建到实际部署的完整流程。压缩包共25个文件,包含jpg/png图像样本、py训练脚本、md说明文档、dat数据…

2026/9/25 0:00:41 阅读更多 →
Vim基础操作全攻略:保存退出、模式切换与高频命令实战

Vim基础操作全攻略:保存退出、模式切换与高频命令实战

1. 项目概述1.1 核心需求解析今天聊聊Vim。写这个题目的原因是:几乎每个后端开发者、运维人员、数据工程师某天都会遇到一个场景——深夜加班,服务器登录界面只有黑底白字,编辑器只有vi/vim,你必须在五分钟内完成一次配置修改并保…

2026/9/25 0:00:41 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/24 14:34:13 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/24 9:10:42 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/24 14:33:56 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/24 12:50:34 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/24 14:33:48 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/24 12:49:17 阅读更多 →