闭包 (Closure) 是什么
本文所讲闭包是计算机中的闭包
如果一上来就一大段理论,估计连我自己也看不懂
以具体例子入手来理解,因为在函数式语言中闭包是一个非常常见的概念,所以我们以一种函数式语言 Scheme 为例来讲解
; 以斜截式的方式定义一条直线 y = ax + b
(define straight-line
(lambda (a b)
(lambda (x)
(+ (* a x) b))))
; 定义一条斜率为 1, 截距为 1 的直线
(define straight-line-1-1 (straight-line 1 1))
; 定义一条斜率为 2, 截距为 2 的直线
(define straight-line-2-2 (straight-line 2 2))
; 求当 x 等于 1 时,这条直线的 y 值
(straight-line-1-1 1) ; => 2
(straight-line-1-1 0) ; => 1
(straight-line-2-2 1) ; => 4
(straight-line-2-2 0) ; => 2
最近正好看了会 Go 语言,下面是 Go 代码,功能和写法都和上面的 Scheme 代码一样。也是我对 Go 语言中闭包的一种练习。如果你习惯于看 C 类的代码,那么 Scheme 代码可能不好理解,相反下面的代码就好理解一点
package main
import "fmt"
// 以斜截式的方式定义一条直线 y = ax + b
func straight_line(a, b float64) func(x float64) float64 {
return func(x float64) float64 {
return a * x + b
}
}
func main() {
// 定义一条斜率为 1, 截距为 1 的直线
straight_line_1_1 := straight_line(1, 1)
// 定义一条斜率为 2, 截距为 2 的直线
straight_line_2_2 := straight_line(2, 2)
// 求当 x 等于 1 时,这条直线的 y 值
fmt.Println(straight_line_1_1(1))
fmt.Println(straight_line_1_1(0))
fmt.Println(straight_line_2_2(1))
fmt.Println(straight_line_2_2(0))
}
// 输出:
// 2
// 1
// 4
// 2
以上面中的 straight-line-1-1
为例,它是什么呢?或者换种问法,我们希望它是什么呢?
至少我希望它是这个东西
(lambda (x)
(+ (* a x) b))))
且 a 为 1, b 为 1
这样我们可以很方便地表示直线,而且将 a 和 b 自由变量绑定到某个值,不至于混乱
东西的样子出来了,我们得给这个东西取个名字吧,就叫「闭包」吧
将其表示地形式一点,如下;
(Closure
'(lambda (x)
(+ (* a x) b))
'((a . 1) (b . 1)))
我们也可以这样说,闭包就是函数和创建这个函数时的环境总和(你也可以理解为相关数据)
而这个环境指的是自由变量,如果一个函数并没有引用自由变量,那么它只是一个函数而已
在这个例子中,函数就是 (lambda (x) (+ (* a x) b))
, 环境就是 ((a . 1) (b . 1))
闭包就是这么个东西,是不是感觉很简单?
都是我们能够很容易理解的概念,没有网上一大段一大段那么复杂,有些东西细细品味确实就简单了
在多说一点,闭包和函数的区别就是:
- 闭包包含了函数和环境
- 函数只是一个过程,并不包括创建这个函数的环境,或者说它也有环境,但是这个环境为空
其实理解了闭包是什么,你也就知道闭包有哪些特性了,在以后的使用过程中不至于出错
你可能又会想到对象这个概念,是的,用闭包可以实现对象的封装,继承等,具体可见:http://okmij.org/ftp/Scheme/oop-in-fp.txt
参考:http://www.yinwang.org/blog-cn/2013/03/26/lisp-dead-alive/