Erlang 从零 · 第 14 课(展开目录)
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 1435 分钟

带着累加器往前走

先手写尾递归求和,再用 `lists:foldl/3` 收拢 list。

01 · 先看完整例子

保存为 totals.erl

编译后再运行 lists:foldl(fun(N, T) -> N + T end, 0, [3,5,7]).。

Erlang
-module(totals).
-export([sum/1]).

sum(Numbers) ->
  sum(Numbers, 0).

%% 空 list 返回最后的累加结果
sum([], Total) ->
  Total;
sum([Number | Rest], Total) ->
  sum(Rest, Total + Number).
先对照结果
  • totals:sum([3,5,7]). 与 fold 都得到 15。
02 · 回头拆代码

从第一行往下读

  1. 公开 sum/1 把初始累加器设为 0。
  2. 非空子句取出一个数,并递归处理剩余 list。
  3. fold 把同一过程交给标准库。
03 · 认清新符号

符号不是暗号

sum(List, Acc)

内部函数同时接收剩余输入与累加结果。

lists:foldl/3

从左到右收拢 list。

fun(Item, Acc) -> ... end

fold 的二参数匿名函数。

04 · 代码里的概念

把名字和意思对上

01

尾递归

递归调用是函数最后一步,BEAM 可复用调用空间。

02

fold

从初始值开始,把 list 元素逐项收进一个结果。

05 · 再说清楚

这些写法为什么有用

递归每一步让输入 list 变短。累加器保存已经算好的部分。

空 list 子句返回累加器,递归在这里停。它应放在非空子句前后都可,但两者要紧挨。

lists:foldl/3 是通用累加工具。匿名函数参数顺序是元素在前、累加器在后。

06 · 自己改一次

合上答案,动手

用 lists:foldl/3 求 [2,3,4] 的乘积。

练习起点
lists:foldl(
  fun(Number, Product) -> ____ end,
  ____,
  [2, 3, 4]
).

目标结果: 得到 24。

卡住了,再看提示

每步相乘,初始值是 1。

运行过以后,再看一种答案
一种答案
lists:foldl(
  %% 把当前数字乘进累加结果
  fun(Number, Product) -> Number * Product end,
  1,
  [2, 3, 4]
).
想一想:求和时为什么用 0 作为初始值?

任何数加 0 不变;空 list 的总和也自然是 0。

带走

记住三句

  1. 1

    递归输入要不断变小。

  2. 2

    累加器保存阶段结果。

  3. 3

    标准库 fold 能减少重复递归。

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