📄 一句话概括
同一件事两种风格:语句式 6 条语句、4 个中间变量;函数式 1 条语句、0 个中间变量,结果都是 220——函数式不是少写变量,是不写名字。
📝 本节和前面几节的关系
A.2 列了 7 条,这一节开始逐条展开:第 1 条最简单,也最能看出风格差别。
一、实测:两种风格的数量差
语句式 6 条语句:建 total、取范围、筛偶数、求平方、求和、返回。 函数式 1 条语句:一条 return,里面三层函数串起来。 语句式 4 个中间变量(total/nums/evens/squares),函数式 0 个。 两边结果都是 220。
二、不写名字才是关键
起名字的代价不是打字,是要管它:这个名字的值在什么时候是什么? 这就是状态——而状态是 bug 的老家:改了一处、忘了另一处。 函数式把这批名字全砍掉,每一步只是【把上一个结果喂给下一个】。
三、代价是什么
可读性:sum(map(f, filter(g, xs))) 得从里往外读。
调试:出错时栈里全是匿名函数,断点不好下手。
所以不是【函数式更好】,是在【不需要状态】的地方它更省心。
四、动手 Demo:特征1:使用函数编写程序
同一件事写两版:语句式与函数式。 用 ast 数语句条数,并确认结果一致。
def state_way():
total = 0
nums = range(1, 11)
evens = [i for i in nums if i % 2 == 0]
squares = [i * i for i in evens]
total = sum(squares)
return total
def func_way():
return sum(map(lambda i: i * i,
filter(lambda i: i % 2 == 0, range(1, 11))))
print(nstmt(state_way), nstmt(func_way)) # 6 1
print(state_way(), func_way()) # 220 220
实跑输出:
--------------------------------------------------------------------
特征1:使用函数编写程序
--------------------------------------------------------------------
同一件事(1~10 里偶数平方和)用两种风格写,**数量:**
语句式:先建中间列表再求和,**每一步都起名字**
函数式:一条 return,里面 3 层函数串起来,**一个名字都不起**
语句式语句条数 6 条
函数式语句条数 1 条
语句式中间变量 4 个(total/nums/evens/squares)
函数式中间变量 0 个
两者结果 220 与 220
**函数式的打法不是【少写变量】,是【不写名字】。**
**因为一旦起了名字,就要管它的值在什么时候是什么**——**这就是状态,也就是 bug 的老家。**
**代价是可读性**:`sum(map(f, filter(g, xs)))` 得从里往外读。
实测:语句式 6 条语句 4 个中间变量,函数式 1 条语句 0 个,结果都是 220。 函数式的打法不是少写变量,是不写名字——不写名字就不用管它的值。 代价是可读性和可调试性,所以只在不需要状态的地方用。
30 秒实验
- 找一个你代码里【建了一堆中间列表】的函数,试着合成一条表达式。
- 再判断一下:合成之后别人还能读懂吗?读不懂就说明该拆开。
常见误区
- 以为函数式就是写得短。短是结果,不是目的。
- 把嵌套三层以上的表达式硬凑出来。那不是函数式,那是炫技。
自测三问(点开对答案)
1. 两版各几条语句? —— 语句式 6 条、函数式 1 条,结果都是 220。 2. 为什么说不写名字是关键? —— 起名字就要管它的值在什么时候是什么,那就是状态,也就是 bug 的老家。 3. 代价是什么? —— 可读性要反向读、调试时栈里全是匿名函数。- ☐ A.4 特征2:所有表达式都返回值
相关笔记
- 函数式语言的7个特征 — 第 1 条的位置
- 特征5:没有副作用 — 不写名字的深层原因
- 将面向对象作为工具熟练掌握 — 另一种取舍
- 🧩 面向对象是怎样工作的 · 索引