Erlang 从零 · 第 28 课(展开目录)
01 · 先打开 erl02 · 先认常见 term03 · 分清两种文字04 · 变量只接一次05 · 拆开一列,再筛一次06 · 把文字安全变成整数07 · 用 case 选一条路08 · 让函数继续走09 · 把代码放进模块10 · 条件必须说真话11 · 一层一层取数据12 · 文字先说清编码13 · 让函数子句排好队14 · 带着累加器往前走15 · 先看值,再选路16 · 给模块立规矩17 · 让 rebar3 管项目18 · 让结果有固定形状19 · 把磁盘当成会失败的邻居20 · 把函数交给 list21 · record 留在院里22 · 先写模块之间的约定23 · 把 term 形状写出来24 · 小测试与整段路25 · 把应用磨成命令26 · 先把小项目圈住27 · 把真实文件洗干净28 · 公开入口越少越稳29 · 把约定留在源码旁30 · 打包,也留下线索31 · 按最初任务交卷
ERLANG · 作品 · LESSON 2845 分钟
公开入口越少越稳
让统计模块只接收行 list,把分词 helper 留在内部。
01 · 先看完整例子
保存为 trail_stats.erl
本项目约定字符数不包含换行,词由 Unicode 空白分隔。
Erlang
-module(trail_stats).
-export([count/1]).
%% 对外只开放 count/1
count(Lines) ->
#{
lines => length(Lines),
words => lists:sum([word_count(Line) || Line <- Lines]),
chars => char_count(Lines)
}.
word_count(Line) ->
length(string:lexemes(Line, " ")).
char_count(Lines) ->
Text = iolist_to_binary(Lines),
length(unicode:characters_to_list(Text)).先对照结果
trail_stats:count([<<"hello beam">>, <<"again">>]).返回行 2、词 3、字符 15。
02 · 回头拆代码
从第一行往下读
- 公开函数返回固定三键 map。
- 两个 helper 未导出,可在模块内部修改。
- 先合并 binary 再按 Unicode 字符计数。
03 · 认清新符号
符号不是暗号
-export([count/1]).只公开一个统计入口。
maps:get/2CLI 可按键读取统计结果。
string:lexemes/2按给定分隔字符把 Unicode 文字拆成词。
04 · 代码里的概念
把名字和意思对上
公开 API
模块外允许依赖的 name/arity 与返回 term。
私有 helper
未导出、只帮助当前模块完成工作的函数。
05 · 再说清楚
这些写法为什么有用
模块公开函数越多,调用者越容易依赖内部细节。本项目只公开真正需要的入口。
统计模块接收 Parser 已经清洗的行,不再读文件。行、词、字符规则集中在这里。
内部 helper 不放进 -export。返回 map 给字段命名,也便于 CLI 读取。
06 · 自己改一次
合上答案,动手
写出空输入的期待 map。
练习起点
?assertEqual(
#{lines => ____, words => ____, chars => ____},
trail_stats:count([])
).目标结果: 三个计数都为 0。
卡住了,再看提示
空 list 没有行、词和字符。
运行过以后,再看一种答案
一种答案
%% 空输入是公开 API 的重要边界
?assertEqual(
#{lines => 0, words => 0, chars => 0},
trail_stats:count([])
).想一想:为什么 `word_count/1` 不放进 export 列表?
它只是 count/1 的实现细节,调用者不需要依赖分词过程。
带走
记住三句
- 1
公开 API 要小而明确。
- 2
模块之间传稳定 term。
- 3
内部 helper 不轻易导出。
本课结束代码也跑过一次,就把这一课收好。