Erlang记录 #
一、记录概述 #
记录(Record)是元组的语法糖,提供了一种命名元组元素的方式,使代码更易读。
1.1 记录的特点 #
- 编译时转换为元组
- 提供字段名称访问
- 支持默认值
- 类型安全(编译时检查)
二、记录定义 #
2.1 基本定义 #
erlang
-module(record_def).
-export([demo/0]).
-record(person, {name, age, city}).
demo() ->
Person = #person{name = "Alice", age = 30, city = "Beijing"},
io:format("Person: ~p~n", [Person]),
ok.
2.2 默认值 #
erlang
-module(record_default).
-export([demo/0]).
-record(user, {
name,
age = 0,
email = undefined,
active = true
}).
demo() ->
U1 = #user{name = "Alice"},
U2 = #user{name = "Bob", age = 25},
U3 = #user{name = "Charlie", age = 30, email = "charlie@example.com"},
io:format("U1: ~p~n", [U1]),
io:format("U2: ~p~n", [U2]),
io:format("U3: ~p~n", [U3]),
ok.
2.3 记录本质 #
erlang
-module(record_essence).
-export([demo/0]).
-record(point, {x, y}).
demo() ->
Point = #point{x = 10, y = 20},
io:format("Record: ~p~n", [Point]),
io:format("As tuple: ~p~n", [{point, 10, 20}]),
io:format("Equal: ~p~n", [Point =:= {point, 10, 20}]),
ok.
记录 #point{x = 10, y = 20} 编译后变成 {point, 10, 20}。
三、记录操作 #
3.1 创建记录 #
erlang
-module(record_create).
-export([demo/0]).
-record(book, {title, author, year = 2024}).
demo() ->
B1 = #book{title = "Erlang Programming"},
B2 = #book{title = "Learn Erlang", author = "John Doe"},
B3 = #book{title = "OTP Guide", author = "Jane Doe", year = 2023},
io:format("B1: ~p~n", [B1]),
io:format("B2: ~p~n", [B2]),
io:format("B3: ~p~n", [B3]),
ok.
3.2 访问字段 #
erlang
-module(record_access).
-export([demo/0]).
-record(person, {name, age, city}).
demo() ->
Person = #person{name = "Alice", age = 30, city = "Beijing"},
Name = Person#person.name,
Age = Person#person.age,
City = Person#person.city,
io:format("Name: ~p~n", [Name]),
io:format("Age: ~p~n", [Age]),
io:format("City: ~p~n", [City]),
ok.
3.3 更新字段 #
erlang
-module(record_update).
-export([demo/0]).
-record(person, {name, age, city}).
demo() ->
P1 = #person{name = "Alice", age = 30, city = "Beijing"},
P2 = P1#person{age = 31},
P3 = P2#person{city = "Shanghai"},
io:format("P1: ~p~n", [P1]),
io:format("P2: ~p~n", [P2]),
io:format("P3: ~p~n", [P3]),
ok.
四、记录模式匹配 #
4.1 基本匹配 #
erlang
-module(record_match).
-export([demo/0]).
-record(person, {name, age, city}).
demo() ->
Person = #person{name = "Alice", age = 30, city = "Beijing"},
#person{name = Name, age = Age} = Person,
io:format("Name: ~p~n", [Name]),
io:format("Age: ~p~n", [Age]),
ok.
4.2 函数参数匹配 #
erlang
-module(record_func_match).
-export([greet/1, is_adult/1]).
-record(person, {name, age}).
greet(#person{name = Name, age = Age}) ->
io:format("Hello ~s, you are ~p years old~n", [Name, Age]).
is_adult(#person{age = Age}) when Age >= 18 -> true;
is_adult(_) -> false.
4.3 特定值匹配 #
erlang
-module(record_value_match).
-export([classify/1]).
-record(user, {name, role = guest}).
classify(#user{role = admin}) -> administrator;
classify(#user{role = moderator}) -> moderator;
classify(#user{role = guest}) -> guest.
4.4 守卫中使用 #
erlang
-module(record_guard).
-export([can_vote/1]).
-record(citizen, {name, age, country}).
can_vote(#citizen{age = Age, country = Country})
when Age >= 18, Country =:= "USA" -> true;
can_vote(#citizen{age = Age, country = Country})
when Age >= 18, Country =:= "China" -> true;
can_vote(_) -> false.
五、记录与函数 #
5.1 工厂函数 #
erlang
-module(record_factory).
-export([new_person/2, new_person/3]).
-record(person, {name, age = 0, city = "Unknown"}).
new_person(Name, Age) ->
#person{name = Name, age = Age}.
new_person(Name, Age, City) ->
#person{name = Name, age = Age, city = City}.
5.2 访问器函数 #
erlang
-module(record_accessor).
-export([get_name/1, get_age/1, set_age/2]).
-record(person, {name, age, city}).
get_name(#person{name = Name}) -> Name.
get_age(#person{age = Age}) -> Age.
set_age(Age, Person) -> Person#person{age = Age}.
5.3 转换函数 #
erlang
-module(record_convert).
-export([to_list/1, from_list/1, to_map/1, from_map/1]).
-record(person, {name, age, city}).
to_list(#person{name = N, age = A, city = C}) ->
[{name, N}, {age, A}, {city, C}].
from_list([{name, N}, {age, A}, {city, C}]) ->
#person{name = N, age = A, city = C}.
to_map(#person{name = N, age = A, city = C}) ->
#{name => N, age => A, city => C}.
from_map(#{name := N, age := A, city := C}) ->
#person{name = N, age = A, city = C}.
六、记录包含 #
6.1 包含记录定义 #
创建 records.hrl 文件:
erlang
-record(person, {name, age, city}).
-record(address, {street, city, country}).
在模块中使用:
erlang
-module(use_include).
-include("records.hrl").
-export([demo/0]).
demo() ->
Person = #person{name = "Alice", age = 30},
io:format("Person: ~p~n", [Person]),
ok.
6.2 从其他模块包含 #
erlang
-module(cross_module).
-include_lib("kernel/include/file.hrl").
-export([file_info/1]).
file_info(Path) ->
{ok, Info} = file:read_file_info(Path),
#file_info{size = Size, type = Type} = Info,
io:format("Size: ~p, Type: ~p~n", [Size, Type]).
七、记录最佳实践 #
7.1 命名规范 #
erlang
-module(record_naming).
-export([demo/0]).
-record(user_state, {id, name, status}).
-record(http_request, {method, url, headers, body}).
-record(db_config, {host, port, database, username, password}).
demo() -> ok.
规范:
- 记录名使用下划线分隔的小写单词
- 字段名使用下划线分隔的小写单词
- 使用有意义的名称
7.2 使用类型规范 #
erlang
-module(record_types).
-export([new_user/2]).
-type name() :: binary() | string().
-type age() :: non_neg_integer().
-record(user, {
name :: name(),
age :: age(),
email :: binary() | undefined
}).
-type user() :: #user{}.
-spec new_user(name(), age()) -> user().
new_user(Name, Age) ->
#user{name = Name, age = Age, email = undefined}.
7.3 使用默认值 #
erlang
-module(record_best_default).
-export([new/0, new/1]).
-record(config, {
host = "localhost" :: string(),
port = 8080 :: non_neg_integer(),
timeout = 5000 :: non_neg_integer(),
debug = false :: boolean()
}).
new() -> #config{}.
new(Overrides) when is_list(Overrides) ->
lists:foldl(
fun({host, V}, C) -> C#config{host = V};
({port, V}, C) -> C#config{port = V};
({timeout, V}, C) -> C#config{timeout = V};
({debug, V}, C) -> C#config{debug = V}
end,
#config{},
Overrides
).
八、记录与元组比较 #
8.1 使用记录 #
erlang
-module(compare_record).
-export([demo/0]).
-record(person, {name, age, city}).
demo() ->
Person = #person{name = "Alice", age = 30, city = "Beijing"},
Name = Person#person.name,
io:format("Name: ~p~n", [Name]),
ok.
8.2 使用元组 #
erlang
-module(compare_tuple).
-export([demo/0]).
demo() ->
Person = {person, "Alice", 30, "Beijing"},
{person, Name, _, _} = Person,
io:format("Name: ~p~n", [Name]),
ok.
8.3 比较表 #
| 特性 | 记录 | 元组 |
|---|---|---|
| 字段名 | 支持 | 不支持 |
| 默认值 | 支持 | 不支持 |
| 编译时检查 | 有 | 无 |
| 代码可读性 | 高 | 低 |
| 运行时开销 | 无 | 无 |
九、实际应用 #
9.1 状态机 #
erlang
-module(state_machine_record).
-export([new/0, connect/1, send_data/2, disconnect/1]).
-record(state, {
status = disconnected :: disconnected | connected,
buffer = [] :: list(),
count = 0 :: non_neg_integer()
}).
new() -> #state{}.
connect(State) ->
State#state{status = connected}.
send_data(Data, State = #state{buffer = Buf, count = Cnt}) ->
State#state{buffer = [Data | Buf], count = Cnt + 1}.
disconnect(State) ->
State#state{status = disconnected, buffer = [], count = 0}.
9.2 配置管理 #
erlang
-module(config_record).
-export([default/0, validate/1]).
-record(app_config, {
db_host :: string(),
db_port :: non_neg_integer(),
db_name :: string(),
api_key :: string(),
log_level :: debug | info | warning | error
}).
default() ->
#app_config{
db_host = "localhost",
db_port = 5432,
db_name = "myapp",
api_key = "",
log_level = info
}.
validate(#app_config{db_host = ""}) -> {error, missing_db_host};
validate(#app_config{db_port = Port}) when Port < 1 orelse Port > 65535 ->
{error, invalid_port};
validate(#app_config{api_key = ""}) -> {error, missing_api_key};
validate(_) -> ok.
9.3 HTTP请求处理 #
erlang
-module(http_record).
-export([new_request/2, add_header/3, get_header/2]).
-record(http_request, {
method :: get | post | put | delete,
url :: string(),
headers = [] :: [{string(), string()}],
body = <<>> :: binary()
}).
new_request(Method, Url) ->
#http_request{method = Method, url = Url}.
add_header(Name, Value, Req) ->
Req#http_request{headers = [{Name, Value} | Req#http_request.headers]}.
get_header(Name, #http_request{headers = Headers}) ->
proplists:get_value(Name, Headers).
十、记录的限制 #
10.1 编译时依赖 #
erlang
-module(record_limitation).
-export([demo/0]).
-record(person, {name, age}).
demo() ->
ok.
记录定义必须在编译时可用,无法在运行时动态创建。
10.2 模块间共享 #
需要在 .hrl 文件中定义,并在每个使用模块中包含:
erlang
-include("person.hrl").
10.3 热升级问题 #
修改记录定义需要重新编译所有使用该记录的模块。
十一、总结 #
本章学习了:
- 记录的定义和本质
- 记录的创建、访问、更新
- 记录模式匹配
- 记录与函数
- 记录包含
- 最佳实践
- 记录与元组比较
- 实际应用
- 记录的限制
准备好学习模式匹配了吗?让我们进入下一章。
最后更新:2026-03-27