第 R2 站 · 可选复习展开探险地图
读懂 Erlang
从大小写开始,再读懂逗号、分号和句点给出的路标。
【语言跳级复习】怎样用 Erlang 重做同一份日志工具,并让两种实现遵守同一输入输出契约?
两份实现通过了各自测试,却给出不同结果
Elixir 与 Erlang 的解析器各自通过测试,遇到空行、WARN 或坏格式时却给出不同结果。原因藏在导出函数、标点、模式和输入约定里。
- 两种实现共用的输入样例与预期结果
- 导出函数列表,以及编译器给出的第一条错误
- 空行、INFO、WARN 与坏格式输入的并排输出
它要解决什么
不少 BEAM 文档和工具使用 Erlang。看懂基础写法,就能读懂错误信息、:gen_tcp 和 observer。先从数据、函数与标点开始。
你会做到
能认出 Erlang 中的 atom、变量、tuple、list、map 和 binary
能用逗号、分号和句点写清“接着做”“换条路”和“到这里结束”
能创建 Rebar3 项目,并用 EUnit 检查自己的函数
- 完成 Elixir 基础,能看懂上一关的日志处理步骤
- 知道模式匹配是在检查数据形状,并见过函数再次调用自己
先看做什么,再看怎么写
Erlang 模块名是 atom。total/1 中的 1 表示函数接收一个参数。列表推导会用形状模板挑出已付款订单,再取金额。
# 计算所有已付款订单的总金额
defmodule Orders do
def total(items) do
# 先筛选状态,再取出金额并相加
items
|> Enum.filter(&(&1.status == :paid))
|> Enum.map(& &1.amount)
|> Enum.sum()
end
end给 Erlang 代码断句
分别改错逗号、分号和句点。别背报错,找出编译器认为哪一段还没结束。
- 01
创建
classify/1,为正数和零或负数准备两个入口 - 02
把第一条入口末尾的分号改成句点,再编译一次
- 03
把同一个函数体中表示“接着做”的逗号改成分号,再编译一次
# 编译项目,并运行全部 EUnit 测试
rebar3 eunit- 分号能隔开同名、同参数个数的多个函数入口
- 句点会告诉编译器:整个函数定义到这里结束
- 逗号会让同一函数体中的表达式按顺序继续
删掉 -export([total/1]).,再从 shell 调用。函数仍在模块里,但外部无法访问。
Erlang 的逗号、分号和句点都在表达代码结构,不能只按好看与否互换。
成功编译只说明结构基本正确,还不能说明程序算出的答案一定正确。
这段代码里的关键词
大写变量
在 Erlang 中,大写字母或下划线开头的名字表示变量;小写裸词通常是一个固定标签,叫 atom。
标点结构
逗号连接顺序表达式,分号隔开函数子句或分支,句点结束完整定义或 shell 表达式。
binary
装着一串字节的数据盒子,写作 <<...>>。UTF-8 文本通常放在 binary 中;传统 charlist 则是一串代表字符的整数。
这里用了哪些设计模式
Contract Test
用同一组样例约束两种语言的输入与输出。
Ports and Adapters
把语言特有实现藏在共同契约后面。
Erlang 看到没有引号的 user_name 时,会把它当成什么?
翻译一段 Erlang
用 Rebar3 重写日志工具,让两种实现读取同一组样例并返回相同结果。
提示 1先迈一步
可以用 binary 的形状模板拆出 INFO、WARN 和 ERROR 前缀。
提示 2再缩小一点
读取文件放在单独模块里,整理日志的核心函数只接收 binary 列表。
提示 3离答案很近了
把上一关的测试场景带过来;EUnit 测试可以使用 _test 或 generator 写法。
提示 4从终点往回想
先挑一条“过关信号”,为它写一个最小测试。如果电脑看不出结果,就把这句话改成一个真正能观察到的现象。
模块只打开真正需要从外部进入的函数入口
代码会明确分清 binary 和 charlist,并在需要时转换
EUnit 检查了与 Elixir 版相同的正常、边角和错误场景
记住三句话
- 1
Erlang 会直接写出模块、函数和参数个数,这些信息就像函数的完整地址。
- 2
binary 和 charlist 不是同一种数据;两种语言交换文本时,要主动看清并转换。
- 3
逗号、分号和句点都是结构路标,不只是书写习惯。