Erlang 从零 · 第 20 课(展开目录)
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 2035 分钟
把函数交给 list
用 `lists:map`、`filter` 与 `foldl` 组成清楚的数据流水线。
01 · 先看完整例子
让每一步都有名字
三个名字对应三种数据形状,调试时可逐个查看。
Erlang
Scores = [42, 60, 78, 91],
%% 先筛选,再变换
Passed = lists:filter(fun(Score) -> Score >= 60 end, Scores),
Boosted = lists:map(fun(Score) -> Score + 5 end, Passed),
%% 最后收成总分
Total = lists:foldl(fun(Score, Sum) -> Score + Sum end, 0, Boosted),
{Boosted, Total}.先对照结果
- 得到
{[65,83,96],244}。
02 · 回头拆代码
从第一行往下读
- filter 的函数必须返回布尔值。
- map 为每个及格分数加 5。
- fold 以 0 为初始值累计总分。
03 · 认清新符号
符号不是暗号
lists:filter/2只保留函数返回 true 的项。
lists:map/2把每项变成一个新值。
lists:foldl/3把 list 收拢为一个结果。
04 · 代码里的概念
把名字和意思对上
高阶函数
接收函数参数或返回函数的函数。
数据流水线
把前一步输出交给下一步的连续变换。
05 · 再说清楚
这些写法为什么有用
高阶函数接收另一个函数。它替你走 list,你只写每一项要做什么。
Erlang 没有 Elixir 的管道运算符,但可以给每一步结果取短名字。名字也能说明数据怎样变化。
map 保持项数,filter 可能减少项数,fold 把多项收成一个结果。
设计选择
为什么这样写
为什么把数据一站一站传下去?
管道把一长串嵌套调用改成从上往下的数据旅程。每一步仍应是能单独理解和测试的小函数。
管道不是更高级的循环。若参数位置别扭、步骤偷偷做大量 IO,拆成普通调用反而更清楚。
换一把尺子
从 Java、Python、JavaScript 看过来
Java
- 熟悉的起点
- Stream 链或连续方法调用。
- BEAM 在意什么
- Elixir
|>只是把左值放进下一次调用;Erlang 通常显式嵌套或给中间值命名。 - 别带错直觉
Enum多半立即计算,Stream才是惰性。
Python
- 熟悉的起点
- 生成器、推导式和逐步赋值。
- BEAM 在意什么
- capture 与 pipe 把小函数组合起来,但不会原地改 list。
- 别带错直觉
&String.trim/1是函数值,String.trim/1只是函数身份写法。
JavaScript
- 熟悉的起点
- map/filter 链、回调或 iterator。
- BEAM 在意什么
- 数据默认是不可变值;每一步交回一个新结果。
- 别带错直觉
- 不要把
&1读成 JS 的位运算与号。
06 · 自己改一次
合上答案,动手
把 [1,2,3,4] 中的偶数平方。
练习起点
Even = lists:filter(fun(N) -> rem(N, ____) == 0 end, [1,2,3,4]),
lists:map(fun(N) -> ____ end, Even).目标结果: 得到 [4,16]。
卡住了,再看提示
偶数除以 2 余 0;平方写 N * N。
运行过以后,再看一种答案
一种答案
Even = lists:filter(fun(N) -> rem(N, 2) == 0 end, [1,2,3,4]),
%% map 只处理筛选后的偶数
lists:map(fun(N) -> N * N end, Even).想一想:`lists:map/2` 会自动丢掉不需要的项吗?
不会。map 为每项产生一项;需要丢项时先用 filter。
带走
记住三句
- 1
函数也能作为参数传递。
- 2
filter、map、fold 各有明确职责。
- 3
用短变量名串起 Erlang 数据流水线。
本课结束代码也跑过一次,就把这一课收好。