函数式编程——闭包

原文链接:https://stackoverflow.com/questions/36636/what-is-a-closure
原文中的sample code是JavaScript写的,本文已经替换为Kotlin。

Question
闭包(closure)是什么?

我问一个关于柯里化(currying)和闭包的问题。闭包是什么?它和柯里化如何联系到一起的?
闭包到底是什么呢?有人说闭包是函数,有人说是栈,也有人说是“隐藏”值(hidden value)。我的理解是函数+封闭变量(function + enclosed variable)。

One answer

变量作用域

当局部变量被申明的时候,就会有一个作用域。一般情况下,局部变量只存在于申明它们的块(block)或者函数。
函数式编程——闭包_第1张图片
如果想访问局部变量,大部分编程语言会在当前作用域查找,如果没有找到,就向父作用域查找,直到根作用域(root scope)。
函数式编程——闭包_第2张图片
当一个块或函数已经执行完成后,局部变量就不再需要了,通常情况下,局部变量会从内存中销毁。

这是通常期望事情发生的方式。

闭包是持久的局部变量作用域。

闭包是一个持久的作用域,这个作用域持有局部变量,即使代码执行已经移出了块(这个作用域依然持有局部变量,不会销毁局部变量)。支持闭包的编程语言(例如JavaScript,Swift,Ruby,Kotlin)允许保持对作用(包括其父作用域)的引用,即使在申明这些变量所在的块已经执行完成后,只要在某处保留对该块或函数的引用。

作用域对象和作用域里的全部局部变量,都与函数绑定在一起,只要函数持久存在,就会持久存在。

这提供了函数的灵活性。在函数再次调用的时候,甚至函数在一个完全不同的上下文被调用,我们期望变量的作用域,没有发生变化,仍然是函数第一次定义的时候变量所在的作用域。

例子

下面提供一个简单例子来阐述这一点(原文是JavaScript写的,本文已经替换为Kotlin)。
函数式编程——闭包_第3张图片
我在一个函数里又定义了一个函数。Inner函数可以访问outer函数的全部局部变量,包括 a。变量ainner函数的作用域里。

正常情况下,一个函数退出,它的局部变量同时销毁。但是,我们返回了inner函数并且赋值给一个变量fnc,即使当outer已经退出了,局部变量依然是持久的。定义inner时作用域内的变量也是持久的。变量a像被包裹起来——它在一个闭包内。

需要注意的是变量afnc完全私有,这是函数式编程语言,例如JavaScript,定义私有变量的一种方式。

你可能已经猜到了,当调用fnc()的时候会打印变量a的值:1。

没有闭包的编程语言,在函数outer退出的时候,变量a会被垃圾回收销毁掉。调用fnc会抛出error因为a已经不在了。

在Kotlin上,变量a是持久的,因为变量的作用域在函数第一次申明的时候产生的,和函数存在的一样持久。

a 属于outer的作用域。Inner的作用域有一个指向outer作用域的父指针(就是a了)。 fnc是一个指向inner的变量。只要fnc存在,a就存在。a在闭包里。

Another answer

我给一个例子(原文是JavaScript写的,本文已经替换为Kotlin)
函数式编程——闭包_第4张图片
makeCounter返回了一个函数,并赋值给变量x,每调用x一次,计数(count)就会加1。因为我们没有为x提供任何参数,x一定保存了计数(count)。基于作用域规则,x知道在哪里找到计数(count)——必须查找它定义的位置来找到值。这个“隐藏”值(count)被称作闭包。
下面是柯里化 (curring)的例子(原文是JavaScript写的,本文已经替换为Kotlin)。
函数式编程——闭包_第5张图片
正如你所看到的,当用参数a(值是3)执行add的时候,这个值(a)保存在了返回函数add3的闭包里。当调用add3的时候,add3知道在哪里找到这个值(a)执行加法。

你可能感兴趣的:(函数式编程,闭包,柯里化,Kotlin)