闭包 (计算机科学)

title: 词源.
content: 彼得·兰丁(Peter Landin)在1964年将术语“闭包”定义为一种包含环境成分和控制成分的实体,用于在他的SECD机器上对表达式求值。 Joel Moses认为是Landin发明了“闭包”这一术语,用来指代某些其开放绑定(自由变量)已经由其语法环境完成闭合(或者绑定)的lambda表达式,从而形成了闭合的表达式,或称闭包。这一用法后来于1975年被Sussman和Steele在定义 Scheme语言的时候予以采纳。 并广为流传。
title: 语义.
title: 闭包和状态表达.
content: 闭包可以用来在一个函数与一组“私有”变量之间建立关联关系。在给定函数被多次调用的过程中,这些私有变量能够保持其持久性。变量的作用域仅限于包含它们的函数,因此无法从其它程序代码部分进行访问。不过,变量的生存期是可以很长,在一次函数调用期间所建立所生成的值在下次函数调用时仍然存在。正因为这一特点,闭包可以用来完成信息隐藏,并进而应用于需要状态表达的某些编程范型中。  不过,用这种方式来使用闭包时,闭包不再具有引用透明性,因此也不再是纯函数。即便如此,在某些“近似于函数式编程语言”的语言,例如Scheme中,闭包还是得到了广泛的使用。
title: 闭包和第一类函数.
content: 典型的支持闭包的语言中,通常将函数当作第一类对象——在这些语言中,函数可以被当作参数传递、也可以作为函数返回值、绑定到变量名、就像字符串、整数等简单类型。例如以下Scheme代码: 在这个例子中,lambda表达式codice_1出现在函数codice_2中。当这个lambda表达式被执行时,Scheme创造了一个包含此表达式以及对codice_3变量的引用的闭包,其中codice_3变量在lambda表达式中是自由变量。  这个闭包接着被传递到codice_5函数。这个函数的功能是重复调用这个闭包以判断哪些书需要增加到列表哪些书需要丢弃。因为闭包中引用了变量codice_3,所以它在每次被codice_5调用时都可以使用这个变量,虽然codice_5可能定义在另一个文件中。  下面是用ECMAScript (JavaScript)写的同一个例子: 这里,关键字codice_9取代了codice_10,codice_11方法取代了codice_5函数,但两段代码的功能是一样的。  一个函数可以创建一个闭包并返回它,如下述JavaScript例子:  因为在这个例子中闭包已经超出了创建它的函数的范围,所以变量codice_13和codice_14将在函数codice_15返回后继续存在。在没有闭包的语言中,变量的生命周期只限于创建它的环境。但在有闭包的语言中,只要有一个闭包引用了这个变量,它就会一直存在。清理不被任何函数引用的变量的工作通常由垃圾回收完成,但对于 C++ 这种没有垃圾收集(起码目前仍没有一个为语言本身所认可的)的语言而言也不是难事——通过一些细致而琐碎的步骤。
title: 闭包的实现.
content: 典型实现方式是定义一个特殊的数据结构,保存了函数地址指针与闭包创建时的函数的词法环境表示(那些非局部变量的绑定)。使用函数调用栈的语言实现闭包比较困难,因而这也说明了为什么大多数实现闭包的语言是基于垃圾收集机制——当然,不使用垃圾收集也可以做到。  闭包的实现与函数对象很相似。  通过将自由变量放进参数表、并扩大函数名字的作用域,可以把一个闭包 / 匿名 / 内部函数变成一个普通的函数,这叫做“Lambda 提升”。例:
title: 各种语言中(类似)闭包的结构.
title: C语言的回调函数.
content: 在C语言中,支持回调函数的库有时在注册时需要两个参数:一个函数指针,一个独立的codice_16指针用以保存用户数据。这样的做法允许回调函数恢复其调用时的状态。这样的惯用法在功能上类似于闭包,但语法上有所不同。
title: gcc对C语言的扩展.
content: gcc编译器对C语言实现了一种闭包的程序特性。
title: C语言扩展:Blocks.
content: C语言 (使用LLVM编译器或苹果修改版的GCC)支持块。闭包变量用__block标记。同时,这个扩展也可以应用到Objective-C与C++中。
title: C++函数对象.
content: C++早期标准允许通过重载codice_17来定义函数对象。这种对象的行为在某种程度上与函数式编程语言中的函数类似。它们可以在运行时动态创建、保存状态,但是不能如闭包一般方便地隐式获取局部变量,并且有“专物专用”的繁琐问题——对于每一段闭包代码都要单独写一个函数对象类。  C++11标准已经支持了闭包,这是一种特殊的函数对象,由特殊的语言结构——lambda表达式自动构建。C++闭包中保存了其代码内全部向外引用的变量的拷贝或引用。如果是对外界环境中的对象的引用,且闭包执行时该外界环境的变量已经不存在(如在调用栈上已经展开),那么可导致未定义行为,因为C++并不扩展这些被引用的外界环境的变量的生命期。示例代码如下:

Summary: 在计算机科学中,闭包(英语:Closure),又称词法闭包(英语:Lexical Closure)或函数闭包(英语:function closures),是在支持头等函数的编程语言中实现词法绑定的一种技术。闭包在实现上是一个结构体,它存储了一个函数(通常是其入口地址)和一个关联的环境(相当于一个符号查找表)。环境里是若干对符号和值的对应关系,它既要包括约束变量(该函数内部绑定的符号),也要包括自由变量(在函数外部定义但在函数内被引用),有些函数也可能没有自由变量。闭包跟函数最大的不同在于,当捕捉闭包的时候,它的自由变量会在捕捉时被确定,这样即便脱离了捕捉时的上下文,它也能照常运行。捕捉时对于值的处理可以是值拷贝,也可以是名称引用,这通常由语言设计者决定,也可能由用户自行指定(如C++)。

Closure (computer programming): In programming languages, a closure, also lexical closure or function closure, is a technique for implementing lexically scoped name binding in a language with first-class functions. Operationally, a closure is a record storing a function together with an environment. The environment is a mapping associating each free variable of the function (variables that are used locally, but defined in an enclosing scope) with the value or reference to which the name was bound when the closure was created. Unlike a plain function, a closure allows the function to access those "captured variables" through the closure's copies of their values or references, even when the function is invoked outside their scope.