函数编程之闭包漫谈(Closure)

函数是什么

  1. >>> def ExFunc(n):
  2. sum=n
  3. def InsFunc():
  4. return sum+1
  5. return InsFunc
  6. >>> myFunc=ExFunc(10)
  7. >>> myFunc()
  8. >>> myAnotherFunc=ExFunc(20)
  9. >>> myAnotherFunc()
  10. >>> myFunc()
  11. >>> myAnotherFunc()
  12. >>>

在这段程序中,函数InsFunc是函数ExFunc的内嵌函数,并且是ExFunc函数的返回值。我们注意到一个问题:内嵌函数InsFunc中引用到外层函数中的局部变量sum,IronPython会这么处理这个问题呢?先让我们来看看这段代码的运行结果。当我们调用分别由不同的参数调用ExFunc函数得到的函数时(myFunc(),myAnotherFunc()),得到的结果是隔离的,也就是说每次调用ExFunc函数后都将生成并保存一个新的局部变量sum。其实这里ExFunc函数返回的就是闭包。

『纯』的函数是没有状态的,加入了闭包以后就变成有状态的了,相对于一个有成员变量的类实例来说,闭包中的状态值不是自己管理,可以认为是『上帝』在管理。

  • 动态作用域,词法作用域
  • 把数据和作用域绑定到一起就是闭包。
  • 将一个上下文的私有变量的生命周期延长的机制。
  • 调用了局部变量函数就是闭包!

引用环境

按照命令式语言的规则,ExFunc函数只是返回了内嵌函数InsFunc的地址,在执行InsFunc函数时将会由于在其作用域内找不到sum变量而出错。而在函数式语言中,当内嵌函数体内引用到体外的变量时,将会把定义时涉及到的引用环境和函数体打包成一个整体(闭包)返回。现在给出引用环境的定义就容易理解了:引用环境是指在程序执行中的某个点所有处于活跃状态的约束(一个变量的名字和其所代表的对象之间的联系)所组成的集合。闭包的使用和正常的函数调用没有区别。

由于闭包把函数和运行时的引用环境打包成为一个新的整体,所以就解决了函数编程中的嵌套所引发的问题。如上述代码段中,当每次调用ExFunc函数时都将返回一个新的闭包实例,这些实例之间是隔离的,分别包含调用时不同的引用环境现场。不同于函数,闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。

  1. val string = "HelloWorld"
  2. // 闭包:函数的运行环境
  3. // 函数的作用域没有被释放,作用域包含了函数运行的状态、变量、本地类、本地函数,这个作用域就是闭包
  4. fun makeFun(): ()->Unit // ()->Unit 函数是 makeFun() 的返回值,即函数的返回值是一个函数
  5. // makeFun()的函数体
  6. {
  7. var count = 0
  8. return fun(){
  9. println(++count)
  10. }
  11. }
  12. fun fibonacci(): Iterable<Long>{
  13. var first = 0L
  14. var second = 1L
  15. return Iterable {
  16. object : LongIterator(){
  17. override fun nextLong(): Long {
  18. val result = second
  19. second += first
  20. first = second - first
  21. return result
  22. }
  23. override fun hasNext() = true
  24. }
  25. }
  26. }
  27. fun main(args: Array<String>) {
  28. val add5 = add(5)
  29. println(add5(2))
  30. }
  31. //fun add(x: Int) = fun(y: Int) = x + y
  32. fun add(x: Int): (Int)-> Int{
  33. data class Person(val name: String, val age: Int)
  34. return fun(y: Int): Int{
  35. return x + y
  36. }
  37. }