CH03-函数式编程
CH03-函数式编程
命令式编程的代码由一些列该变全局状态的语句构成,而函数是编程则是将计算过程抽象成表达式求值。这些表达式由纯数学函数构成,这些作为一类对象(可以像操作数值一样操作函数)的数学函数没有副作用。因为没有副作用,函数式编程可以更容易做到线程安全,因此尤其适合用于并发编程。同时,函数式编程也是一个直接支持并行的模型。
函数式
线程与锁的模型中,核心是共享可变状态。而对不可变的数据,多线程不使用锁就可以安全的访问。
抛弃可变状态
- 可变状态的风险
- 被隐藏的可变状态
- 逃逸的可变状态
惰性
惰性序列不仅意味着仅在需要时候才生成尾元素,还意味着序列的头元素在使用后可以被丢弃。
函数式并行
基于 Clojure 的语言特性。
总结
- pmap 可以将映射操作并行化,构造一个半懒惰的 map。
- 利用 partition-all 可以对并行的映射操作执行批处理,从而提高效率。
- fold 使用分而治之的策略,可以将 reduce 操作并行化。
- clojure.core.reducers 包内提供的类似 map、mapcat、filter 的函数返回的不是序列,而是 reduciable,这是简化操作的关键。
函数式并发
在 Java 这类命令式语言中,求值顺序与源码的语句顺序紧密相关,虽然编译器和运行时都可能造成一些乱序,但一般来说,求值顺序与其在代码中的顺序基本一致。
函数式语言更有一种声明式的风格。函数式程序并不是描述“如何求值以得到结果”,而是描述“结构应当是什么样的”。因此,在函数式编程中,如何安排求值顺序来获得最终结果是相对自由的,这正是函数式编程可以轻松实现并行的关键所在。
引用透明性
指的是,在任何调用函数的位置,都可以使用函数运行的结构来替换函数的调用,而不会对程序产生影响。
虽然 Java 中有些操作也可以达到这样的效果,但函数式编程中的每个函数都具有引用透明性。当然,除了带有副作用的函数。
数据流
如上图中所示的数据流,由于 (+ 1 2) 和 (+ 3 4) 之间没有依赖关系,所以理论上这两步求值能以任意顺序进行,包括同时执行。前两步求值得到结果后,最优异步加法才能进行。
理论上,运行时可以从这幅图的左端出发,向右端推进数据。当一个函数所依赖的数据都可用时,该函数就可以执行了。至少在理论上所有函数都可以同时执行。这种方式被称为“数据流式编程”。
Clojure 语言中提供了 future 和 promise 来支持这种执行方式。即以操作数据流的形式完成并发编程任务。
总结
许多人对并行编程的理解存在一个误区:认为并行一定会伴随着不确定性,如果不串行执行就不能依赖某一种执行顺序的结果,必须时刻警惕竟态条件。
当然,有一些并发程序一定会带有不确定性。这对它们来说是不可避免的——有一些场景天生就依赖时序。但这并不意味着所有的并行程序都具有不确定性。
在使用线程与锁模型的程序中,大多数潜藏的竟态条件并不来自于问题本身的不确定性,而是因此在解决方案的细节中。
函数式编程具有引用透明性,因此可以随意改变其执行顺序,而不会对最终结果产生影响。我们可以顺理成章的让互相独立的函数并行执行。
关于函数式
开发者对编程语言的偏好很大程度上取决于语言的类型系统。使用 Java、Scala 之类的静态类型语言,与使用 Python、Ruby 之类的动态类型语言的体验是完全不同的。
静态类型语言强迫开发者在早期就必须选择正确的类型。只有付出这样的代价,编译器才能确保运行时不会发生类型错误,同时类型系统还可以优化执行效率。
在函数式编程中也存在这样的分歧。像 Haskell 这种静态类型的函数式语言利用 单子 和 幺半群 等数学概念为类型系统增加了以下能力:明确限制了某些函数和某些值可以使用的位置,在保持函数性的同时能够检测代码的副作用。
但 Clojure 并不拥有静态类型系统。
想要深入学习函数式理论可以尝试学习 Haskell,如《趣学 Haskell》。
想要在生产中应用函数式编程则可以尝试学习 Scala,如《Scala 函数式编程》。
优点
函数式编程的最大好处是我们可以确信程序会按照我们预想的方式运行。一旦上手,比起等价的命令式程序,函数式会更加简单、更易推理、更易测试。
如果采用了函数式解决方案,利用函数式的引用透明性,可以轻松将程序并行化,或者将程序应用于并发环境。由于函数式的不可变特性,大部分存在于线程与锁的 BUG 将销声匿迹。
缺点
很多人认为函数式代码比起命令式代码的效率低。对于某些场景确实存在性能损失,但大部分性能损失是远低于预期的。而且用少许性能损失来换取健壮性和扩展性的提升是值得的。
而且,函数式的优点也远远不止于体现在并发编程上。
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.