在函数编程中经常用到闭包。闭包是什么,它是怎么产生的及用来解决什么问题呢。给出字面的定义先:闭包是由函数及其相关的引用环境组合而成的实体(即:闭包=函数+引用环境)。这个从字面上很难理解,特别对于一直使用命令式语言进行编程的程序员们。本文将结合实例代码进行解释。
函数是什么
地球人都知道:函数只是一段可执行代码,编译后就“固化”了,每个函数在内存中只有一份实例,得到函数的入口点便可以执行函数了。在函数式编程语言中,函数是一等公民(First class value:第一类对象,我们不需要像命令式语言中那样借助函数指针,委托操作函数),函数可以作为另一个函数的参数或返回值,可以赋给一个变量。函数可以嵌套定义,即在一个函数内部可以定义另一个函数,有了嵌套函数这种结构,便会产生闭包问题。如:
package main
import "fmt"
func AntherExFunc(n int) func() {
n++
return func() {
fmt.Println(n)
}
}
func ExFunc(n int) func() {
return func() {
n++
fmt.Println(n)
}
}
func main() {
myAnotherFunc:=AntherExFunc(20)
fmt.Println(myAnotherFunc) //0x48e3d0
myAnotherFunc() //21
myAnotherFunc() //21
myFunc:=ExFunc(10)
fmt.Println(myFunc) //0x48e340
myFunc() //11
myFunc() //12
}
我们来一点点分析一下这些代码,我们可以先把myAnotherFunc、myFunc变量的类型打印出来,可以看到他们的类型是地址。你可以理解为这个地址指向一个背包,这个背包里面装着一些变量和一个函数,就如myAnotherFunc变量背包里面装着n变量和fmt.Println(n)函数。当在main函数中调这行代码的时候myAnotherFunc:=AntherExFunc(20),这个背包就形成了,myAnotherFunc()代码是什么意思呢?就是说我在背包里面拿出函数对这个背包里面的变量进行一系列的操作,并且把这个变量返回出去。
那我为什么又定义了一个ExFunc()函数呢?仔细看代码就知道了,我调用了两次myFunc()函数,返回的是11和12,而调用了两次myAnotherFunc()函数返回的都是21,为什么会这样呢?大家要记住两点
1、内函数(ExFunc函数中的return语句)对外函数的变量(n)的修改,是对变量的引用
2、变量被引用后,它所在的函数结束,这变量也不会马上被销毁
知道这两点就可以理解myAnotherFunc()函数两次调用结果都是一样的了。因为因为他根本就没有对n变量修改,那你又要问一句可是他上面有n++这行代码啊????这你又怎么去解释呢?这个很好解释啦,我在main()函数调用100W次myAnotherFunc()函数,只是调用相应背包内的函数,n++这行代码根本就没有跑,他就相当于一个变量保存在这个背包中。好好理解一下。
最后给出引用环境的定义
在函数式语言中,当内嵌函数体内引用到体外的变量时,将会把定义时涉及到的引用环境和函数体打包成一个整体(闭包)返回。现在给出引用环境的定义就容易理解了:引用环境是指在程序执行中的某个点所有处于活跃状态的约束(一个变量的名字和其所代表的对象之间的联系)所组成的集合。闭包的使用和正常的函数调用没有区别。
由于闭包把函数和运行时的引用环境打包成为一个新的整体,所以就解决了函数编程中的嵌套所引发的问题。如上述代码段中,当每次调用ExFunc函数时都将返回一个新的闭包实例,这些实例之间是隔离的,分别包含调用时不同的引用环境现场。不同于函数,闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。
参考:
http://www.cnblogs.com/Jifangliang/archive/2008/08/05/1260602.html
http://blog.sina.com.cn/s/blog_487109d101018fcx.html#cmt_52E117EA-7F000001-6EA6BB38-922-8A0