闭包概念考证
Opened this issue · 17 comments
闭包这个概念第一次出现在1964年的《The Computer Journal》上,由P. J. Landin在《The mechanical evaluation of expressions》一文中提出了applicative expression和closure的概念。(在计算机领域,closure还有两个完全不同的意义:计算几何中的凸包,和编译领域的包含集)
文中AE的概念定义如下:
We are, therefore, interested in a class of expressions about any one of which it is appropriate to ask the following questions:
Q1. Is it an identifier? If so, what identifier?
Q2. Is it a λ-expression? If so, what identifier or identifiers constitute its bound variable part and in what arrangement? Also what is the expression constituting its λ-body?
Q3. Is it an operator/operand combination? If so, what is the expression constituting its operator? Also what is the expression constituting its operand?
We call these expressions applicative expressions (AEs).
简单翻译
这样,我们对于能够适合提问以下问题的一类特性表达式感兴趣:
Q1. 它是否是标识符? 如果是的话,是什么标识符?
Q2. 它是否是λ表达式? 如果是的话,哪个或者哪些的标识符构成了它的绑定变量部分,范围是什么?还有是什么表达式构成了它的λ-body?
Q3. 它是否是操作符/操作数的组合?如果是的话,构成它的操作符的表达式是什么?还有构成它的操作数的表达式又是什么?
我们称这一类表达式为“适用表达式”(AE)。
在AE的基础上,闭包定义为
Also we represent the value of a λ-expression by a bundle of information called a "closure", comprising the λ-expression and the environment relative to which it was evaluated. We must therefore arrange that such a bundle is correctly interpreted whenever it has to be applied to some argument. More precisely:
a closure has an environment part which is a list whose two items are:
(1) an environment
(2) an identifier or list of identifiers
and a control part which consists of a list whose sole item is an AE.
翻译如下
此外,我们把带有一系列信息的λ表达式称作"闭包",其中包括λ表达式和它执行时的相关连的环境。于是我们要确保无论当它被用于什么参数时,这些信息都能够被正确解析。更精确地:
闭包包含环境部分,它是个只有两项的list,这两项分别是:
(1)环境
(2)一个标识符或者标识符列表
以及控制部分,他是个只有一项的list,唯一的项是个AE
在1975年,Scheme最早实现了闭包。
60年代,因为受到函数式思维影响,计算机软件领域的"表达式"一般是包括λ表达式,也就是函数式语言中的函数的。所以这个闭包的定义在80年代后被更多过程式语言借鉴,其中包括C语言的static全局变量机制、C++的成员函数、JavaScript的局部函数。对于过程式语言中,因为很少有函数以外的"表达式",所以闭包这个概念更多被用于指函数。
在JavaScript中,我们称函数对象为闭包。根据ECMA-262规范,JavaScript的函数包含一个[[scope]]属性。[[scope]]指向scope chain(ECMA-262v3)或者Lexical Environment(ECMA-262v5)。这对应于闭包的环境部分,[[scope]]中可访问的属性列表即是标识符列表,对象本身的引用则对应于环境。控制部分即是函数对象本身了。
winter老师说得好
+1024
winter老师说的好
+1024
1024
“在JavaScript中,我们称函数对象为闭包。”,弱弱的问一句,一般的函数对象(例如这个空函数:function fd(){})也是闭包吗?精准的定义&来源没有研究过,只是现在人们讨论较多的“闭包“前提应该是“其所在环境内应该有一些自由变量的引用”吧?
1024
@AstralRoad @AKIo0O 俩初音……
要消化这个还得花点时间、
@wintercn 老师
[[scope]]指向scope chain(ECMA-262v3)或者Lexical Environment(ECMA-262v5)。这对应于闭包的环境部分,
[[scope]]中可访问的属性列表即是标识符列表,对象本身的引用则对应于环境。控制部分即是函数对象本身了。
这段话结合上面的概念,对闭包的认识就很清晰了。
Lexical Environment是个烂设计,极易造成内存泄漏。
学习了
总结得好棒~!!!!不过似乎有点细节方面的小问题:
在1975年,Scheme最早实现了闭包。
如果将“封装在一起的过程和状态”理解为closure,那么所有OOP的对象都是closure,自然会比1975早,例如smalltalk。
狭义的、原始意义上的闭包其实实现得还更早些。函数定义时的“函数+环境”(也就是lexically scoped closure),在1962年的LISP 1.5中就已经实现,具体可参考LISP 1.5 Programmer's Manual,附录B里面apply和eval处理FUNARG
和FUNCTION
的情况即是lexical closure。
bravo
(https://www.zhoulujun.cn/html/webfront/ECMAScript/js/2015_0814_240.html 闲话闭包)
维基百科上对闭包的解释就很经典:
在计算机科学中,闭包(Closure)是词法闭包(Lexical Closure)的简称,是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。 Peter J. Landin 在1964年将术语闭包定义为一种包含环境成分和控制成分的实体。
百度百科:
闭包是可以包含自由(未绑定到特定对象)变量的代码块;这些变量不是在这个代码块内或者任何全局上下文中定义的,而是在定义代码块的环境中定义(局部变量)。“闭包” 一词来源于以下两者的结合:要执行的代码块(由于自由变量被包含在代码块中,这些自由变量以及它们引用的对象没有被释放)和为自由变量提供绑定的计算环境(作用域)。在 Scala、Scheme、Common Lisp、Smalltalk、Groovy、JavaScript、Ruby、 Python、Go、Lua、objective c、swift 以及Java(Java8及以上)等语言中都能找到对闭包不同程度的支持。
在离散数学(具体的说是抽象代数)里,如果对一个集合中的每个元素执行某个运算操作,得到的结果还是这个集合的元素,那么就说该集合在这个运算操作下构成闭包。例如,整数集合在减法运算下构成闭包;但是自然数在减法运算下不构成闭包。
封闭的定义
有了集合和运算的概念,就可以定义封闭的概念了。
非正式地,如果定义于集合A上的运算+的运算结果仍然属于A,那么运算+对于集合A是封闭的。下面给出“封闭”的一个半形式化定义:
如果对于任意a1,a2∈A,有a1+a2∈A,那么说二元运算+对于集合A是封闭的。(⊙,+ 与”,“或”)
例如“+”对于N+是封闭的,因为任意两个正整数的和结果仍然是正整数;但是“>”对于N+不是封闭的,例如3和5属于N+,但是:3>5=>2∉N+。
闭包性质
一个集合满足闭包性质当且仅当这个集合在某个运算或某些运算的搜集下是封闭的,其中“某些运算的搜集下封闭”是指这个集合单独闭合在每个运算之下。
值得一提的是,之前这条定义往往被作为一条公理引入一个代数结构,叫做“闭包公理”。但是现代集合论往往将运算形式化的定义为集合间的运算,所以将其作为公理引入代数结构是多余的(因为可以通过其它公理间接定义闭包公理),但是对于子集是否闭合的问题,闭包公理仍然有意义。