函数式编程范式简介
欢迎来到其中一种最有趣的编程思考方式!到目前为止,你可能已经习惯了程序式编程(procedural programming)(即给电脑一系列逐步执行的指令)或面向对象编程(object-oriented programming)(即创建相互互动的对象)。 在本章中,我们将探讨函数式编程范式(functional programming paradigm)。我们不再逐步告诉电脑“如何”做某事,而是通过使用数学函数,专注于“想要达成什么”。如果起初觉得这有点不同,不用担心——把它想象成学习一种解决难题的新方法吧!1. 什么是函数式编程?
你过去编写的大部分程序都是指令式(imperative)的。这意味着你会改变程序的“状态”(例如修改变量的值)。 函数式编程则是声明式(declarative)的。在这种风格中,我们将计算过程视为数学函数的求值(evaluation),并避免改变数据。主要区别:
- 无副作用(No Side Effects):函数应该只回传一个值。它不应改变自身外部的变量(例如全局变量)。
- 不变性(Immutability):一旦设定了数值,它就不会改变。与其修改一个列表,不如创建一个带有更改的“新”列表。
- 无状态(Stateless):程序不像循环计数器那样追踪“当前状态”。
快速复习:指令式 = “如何”做。声明式/函数式 = “做什么”。
2. 作为一等公民的函数(Functions as First-Class Objects)
在函数式编程中,函数是一等公民(first-class objects)。这听起来很高级,但其实意思很简单:函数被视为与变量完全一样!你可以对“一等公民”函数做什么?
- 将函数指派给变量。
- 将函数作为参数传递给另一个函数。
- 从另一个函数中回传一个函数作为结果。
3. 函数应用与组合
为了理解函数如何协同工作,我们使用两个主要概念:应用(Application)与组合(Composition)。函数应用(Function Application)
这只是将参数提供给函数的过程。 例子:如果我们有一个函数 \( f(x) = x + 1 \),那么将它应用于数字 \( 5 \),就会得到 \( 6 \)。我们写作 \( f(5) \)。函数组合(Function Composition)
这是我们结合两个函数以产生新函数的方法。一个函数的输出会成为下一个函数的输入。 如果我们有: \( f(x) = x + 1 \) \( g(x) = x \times 2 \) 组合 \( f(g(x)) \) 意味着我们首先将数字加倍,然后加 1。 如果 \( x = 3 \): 1. \( g(3) = 6 \) 2. \( f(6) = 7 \)记忆小撇步:将组合想象成接力赛。第一位选手(函数 G)将接力棒(结果)交给第二位选手(函数 F)来完成任务。
4. 定义域、陪域与映射
在函数式范式中,我们使用数学语言来描述数据如何流动。- 定义域(Domain):所有可能输入值的集合。
- 陪域(Codomain):所有可能输出值的集合。
- 映射(Mapping):将定义域中的输入链接到陪域中输出的规则。
关键重点:函数 \( f: A \to B \) 意味着函数 \( f \) 将集合 \( A \)(定义域)中的值映射到集合 \( B \)(陪域)。
5. 高阶函数(Higher-Order Functions)
高阶函数是指将另一个函数作为参数、回传一个函数,或两者皆是的函数。这是函数式编程变得非常强大的地方!你需要掌握三个主要的函数:A. Map(映射)
Map 接收一个函数和一个列表。它将该函数应用于列表中的每一个项目,并回传一个新列表。 例子:对列表 \([1, 2, 3]\) 使用“翻倍”功能,结果为 \([2, 4, 6]\)。B. Filter(筛选)
Filter 接收一个条件(一个回传 True/False 的函数)和一个列表。它回传一个仅包含符合条件项目的新列表。 例子:对 \([1, 2, 3, 4]\) 使用“是否为偶数?”功能,结果为 \([2, 4]\)。C. Reduce(或 Fold,归约)
Reduce 接收一个列表,并透过重复应用一个函数,将其“浓缩”为单一值。 例子:对 \([1, 2, 3, 4]\) 使用“相加”功能,结果为 \( 10 \) (\(1+2+3+4\))。你知道吗?像 Google 这样的大型科技公司使用“MapReduce”来同时处理成千上万台电脑上的海量数据!
6. 列表:头(Head)与尾(Tail)
函数式语言处理列表的方式通常与你习惯的数组不同。它们将列表拆分为两部分:- Head(头):列表的第一个元素。
- Tail(尾):一个列表,包含除头部之外的所有其余元素。
总结检查清单
你能解释……- 指令式与声明式编程的区别吗?
- 函数作为一等公民是什么意思?
- 组合是如何结合两个函数的?
- 定义域与陪域的定义吗?
- Map、Filter 和 Reduce 是如何运作的?
- 列表的 Head 与 Tail 有什么区别?
如果起初觉得这些很棘手,别担心!函数式编程是一种思维上的转换。继续练习“头/尾”逻辑和高阶函数,很快你就会豁然开朗!