编程语言
119
函数编程之闭包漫谈(Closure)
函数是什么
>>> def ExFunc(n): sum=n def InsFunc(): return sum+1 return InsFunc >>> myFunc=ExFunc(10) >>> myFunc() >>> myAnotherFunc=ExFunc(20) >>> myAnotherFunc() >>> myFunc() >>> myAnotherFunc() >>>
在这段程序中,函数InsFunc是函数ExFunc的内嵌函数,并且是ExFunc函数的返回值。我们注意到一个问题:内嵌函数InsFunc中引用到外层函数中的局部变量sum,IronPython会这么处理这个问题呢?先让我们来看看这段代码的运行结果。当我们调用分别由不同的参数调用ExFunc函数得到的函数时(myFunc(),myAnotherFunc()),得到的结果是隔离的,也就是说每次调用ExFunc函数后都将生成并保存一个新的局部变量sum。其实这里ExFunc函数返回的就是闭包。
『纯』的函数是没有状态的,加入了闭包以后就变成有状态的了,相对于一个有成员变量的类实例来说,闭包中的状态值不是自己管理,可以认为是『上帝』在管理。
- 动态作用域,词法作用域
- 把数据和作用域绑定到一起就是闭包。
- 将一个上下文的私有变量的生命周期延长的机制。
- 调用了局部变量的函数就是闭包!
引用环境
按照命令式语言的规则,ExFunc函数只是返回了内嵌函数InsFunc的地址,在执行InsFunc函数时将会由于在其作用域内找不到sum变量而出错。而在函数式语言中,当内嵌函数体内引用到体外的变量时,将会把定义时涉及到的引用环境和函数体打包成一个整体(闭包)返回。现在给出引用环境的定义就容易理解了:引用环境是指在程序执行中的某个点所有处于活跃状态的约束(一个变量的名字和其所代表的对象之间的联系)所组成的集合。闭包的使用和正常的函数调用没有区别。
由于闭包把函数和运行时的引用环境打包成为一个新的整体,所以就解决了函数编程中的嵌套所引发的问题。如上述代码段中,当每次调用ExFunc函数时都将返回一个新的闭包实例,这些实例之间是隔离的,分别包含调用时不同的引用环境现场。不同于函数,闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。
val string = "HelloWorld" // 闭包:函数的运行环境 // 函数的作用域没有被释放,作用域包含了函数运行的状态、变量、本地类、本地函数,这个作用域就是闭包 fun makeFun(): ()->Unit // ()->Unit 函数是 makeFun() 的返回值,即函数的返回值是一个函数 // makeFun()的函数体 { var count = 0 return fun(){ println(++count) } } fun fibonacci(): Iterable<Long>{ var first = 0L var second = 1L return Iterable { object : LongIterator(){ override fun nextLong(): Long { val result = second second += first first = second - first return result } override fun hasNext() = true } } } fun main(args: Array<String>) { val add5 = add(5) println(add5(2)) } //fun add(x: Int) = fun(y: Int) = x + y fun add(x: Int): (Int)-> Int{ data class Person(val name: String, val age: Int) return fun(y: Int): Int{ return x + y } }