欢迎来到函数式编程(Functional Programming)的世界!
在本章中,我们将探索一种全新的计算机指令编写方式。你之前接触过的绝大多数程序设计方式,很可能都是指令式(Imperative)——也就是向计算机提供一系列逐步执行的指令。而函数式编程(Functional Programming)则大不相同!它就像使用一套数学公式,将数据转换成你想要的结果。
如果刚开始觉得这有点像“数学课”,请别担心。一旦你看懂了当中的逻辑模式,你就会发现这是一种非常简洁且强大的写码方式,而且能大幅减少代码中的错误(bugs)!
1. 函数式 vs. 指令式编程
要理解函数式编程,我们首先要看看它“不是”什么。
• 指令式编程:你通过改变程序的“状态”(使用随时间改变值的变量)来告诉计算机“如何”做某事。比喻:就像食谱,你需要改变食材的状态(切碎洋葱、炒牛肉)。
• 函数式编程:这属于声明式(Declarative)。你通过应用函数来告诉计算机你“想要”什么结果。你不改变原始数据,而是创建新的数据。比喻:就像数学公式 \( f(x) = x + 2 \)。如果 \( x \) 是 5,答案永远是 7。它不会改变 5 本身,只是为你产生一个 7。
函数式编程的核心特性
无状态(Statelessness):在函数式编程中,我们避免改变“状态”。这意味着我们不会使用随程序执行而改变数值的全局变量。
无副作用(Side-Effect Free):函数应该只做一件事:接收输入并返回输出。它不应该“干预”或改变其他东西,例如将结果打印在屏幕上或更新数据库。
引用透明(Referential Transparency):这是一个专业术语,意思就是如果你用相同的输入调用一个函数,你“永远”会得到相同的输出。这让程序变得非常可预测且可靠!
快速回顾:
• 指令式:告诉计算机“如何”做(逐步执行,改变变量)。
• 函数式:告诉计算机“做什么”(使用函数,无副作用)。
• 引用透明:相同的输入 = 每次都得到相同的输出。
2. 函数作为一等对象(First-Class Objects)
在 Haskell 或 Python(以函数式风格使用时)等语言中,函数被视为一等对象(First-Class Objects)。这意味着你可以像对待整数(integer)或字符串(string)等数据一样对待函数。
你可以做到:
1. 将函数作为参数(argument)传递给另一个函数。
2. 将函数作为另一个函数的结果返回。
3. 将函数指派给一个变量。
比喻:把函数想象成“工具”。在程序式编程中,工具被锁在棚子里;而在函数式编程中,你可以把工具放进盒子里、交给朋友,甚至用一个工具来制造另一个工具!
重点总结:如果你能像对待变量一样对待函数,那么它就是一个一等对象。
3. 函数应用(Function Application)
在函数式编程中,我们使用函数应用。这只是将参数提供给函数以产生结果的过程。
我们通常将其写作 \( f \, x \),其中 \( f \) 是函数,\( x \) 是参数。
示例:如果我们有一个名为 square 的函数,那么 square 5 的结果就是 25。
冷知识:在函数式语言中,我们通常不会在调用函数时使用括号!与其写 square(5),我们直接写 square 5 即可。
4. 高阶函数(Higher-Order Functions)
这就是最精彩的部分了!高阶函数是指能接收另一个函数作为参数,或将函数作为结果返回的函数。在你的教学大纲中,你需要掌握以下三个主要的函数:
A. Map
Map 接收一个函数和一个列表(list),它会将该函数应用于列表中的“每一个项目”,并返回一个新的列表。
示例:将 map 与“双倍(Double)”函数搭配列表 [1, 2, 3] 使用,即可得到 [2, 4, 6]。
B. Filter
Filter 接收一个条件(一个返回 True 或 False 的函数)和一个列表。它会检查列表中的每个项目,并只保留符合条件的项目。
示例:将 filter 与“判断偶数(IsEven)”函数搭配 [1, 2, 3, 4] 使用,即可得到 [2, 4]。
C. Reduce(或称 Fold)
Reduce 接收一个列表,并透过重复应用函数将其“缩减”为单一数值。
示例:将 reduce 与“加法(Addition)”函数搭配 [1, 2, 3, 4] 使用,即可得到 10(因为 1+2+3+4 = 10)。
记忆小撇步:厨房比喻
• Map:处理篮子里“所有”蔬菜的切菜动作。
• Filter:只从篮子里挑出“成熟的”番茄。
• Reduce:把所有蔬菜丢进锅子里炖成“一锅”汤。
5. 操作列表(Lists)
在函数式编程中,列表非常重要。我们通常将其拆分为三个部分:
1. 首项(Head):列表中的第一个元素。
2. 尾项(Tail):除了首项之外,“所有其余项目”组成的列表。
3. 空列表(Empty List):什么都没有的列表,通常表示为 [ ]。
示例:在列表 [10, 20, 30, 40] 中:
• 首项(Head)是 10。
• 尾项(Tail)是 [20, 30, 40]。
常见错误:学生常以为“尾项”只是最后一个项目(40)。并非如此!尾项是“列表剩下的部分”。如果你取出尾项 [20, 30, 40] 的首项,你会得到 20!
重点总结:每个非空列表都有一个首项(一个元素)和一个尾项(另一个列表)。
总结与建议
• 函数式编程是声明式的,并且避免了副作用。
• 一等对象意味着函数可以像数据一样被传递。
• 高阶函数(Map, Filter, Reduce)让处理列表变得轻而易举。
• 列表由首项和尾项组成。
考试小撇步:如果考题问到为什么函数式编程对于多核处理器很有用,请记得:因为它没有副作用且没有共享状态,所以程序的不同部分可以同时在不同核心上执行,而互不干扰!