学习 Clojure/函数式编程
函数式编程是编程中那些不幸的术语之一,它笼罩在滥用和混乱中,但实际上并不应该那么神秘。最适合理解为命令式编程的另一种范式,函数式编程的特点是
- 无副作用函数
术语“函数式”来自数学,在数学中,函数严格地只根据零个或多个输入值返回值,并且不产生副作用,也不根据除传递给它的值以外的任何值更改其行为。这样的“纯”函数在每次使用相同的值集调用时都返回相同的东西。
这与命令式(非函数式)编程中函数的工作方式不同。在命令式代码中,函数通常
- 产生副作用(例如写入文件或某个 I/O 设备)
- 修改其输入和全局变量
- 根据函数之外的状态更改其行为
纯函数有两个主要优点
- 避免修改本地数据并避免读取或修改全局数据的函数更容易理解,并且更适合正确性证明。
- 避免读取和写入共享状态的函数可以在没有并发性通常的担忧的情况下并发运行。
现在,对编写所有函数以在没有副作用的情况下运行的明显反对意见是,我们需要副作用才能使我们的程序最终执行任何有用的操作。正如谚语所说,没有副作用的程序只会让你的 CPU 变热。因此,函数式编程中的理想目标只是隔离状态更改,而不是完全消除它们。在像 Haskell 这样的纯函数式语言中,函数纯度由编译器强制执行,这样所有潜在的副作用都必须在代码中明确地限定。但是,像 Clojure 这样的不纯函数式语言不会执行这种强制:Clojure 帮助你以函数式的方式构建代码,但最终取决于你来将副作用排除在你的纯函数式代码之外。
- 不可变数据
如果函数要避免修改状态,如果它们要避免依赖修改状态,那么将尽可能多的数据设为不可变是有意义的:如果数据不能更改,那么它可能不会更改的危险就不存在。在 Clojure 中,标准集合类型是不可变的,实际上,即使局部变量也是不可变的:一旦定义,绑定到局部的值就不会改变。
你可能在想这会让 Clojure 听起来不可用,但令人惊讶的是,在没有可变数据的情况下可以完成多少工作。确实,现实世界中的程序不能完全具有不可变的数据,但与只习惯命令式编程的程序员普遍认为的相比,可以使更多的数据变得不可变。
- 一等函数
在函数式编程中,函数是“一等”,即函数本身是值,可以作为参数传递或存储在变量中。许多动态语言——Javascript、Ruby、Python 等——都有一等函数,但这些语言通常不被认为是函数式语言。
- 基于函数的控制流
在命令式语言中,控制流是通过特殊结构实现的,例如if-else 语句。在函数式语言中,控制流机制要么是函数,要么至少是函数类似的,因为它们返回值。例如,Clojure 中的if 执行两个分支之一,并返回该分支返回的值。这样的结构允许使用否则需要修改变量的习语。