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 · 回头拆代码

从第一行往下读

  1. filter 的函数必须返回布尔值。
  2. map 为每个及格分数加 5。
  3. fold 以 0 为初始值累计总分。
03 · 认清新符号

符号不是暗号

lists:filter/2

只保留函数返回 true 的项。

lists:map/2

把每项变成一个新值。

lists:foldl/3

把 list 收拢为一个结果。

04 · 代码里的概念

把名字和意思对上

01

高阶函数

接收函数参数或返回函数的函数。

02

数据流水线

把前一步输出交给下一步的连续变换。

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. 1

    函数也能作为参数传递。

  2. 2

    filter、map、fold 各有明确职责。

  3. 3

    用短变量名串起 Erlang 数据流水线。

本课结束代码也跑过一次,就把这一课收好。