闭包的基本概念 闭包是可以包含自由(未绑定)变量的代码块;这些变量不是在这个代码块或者任何全局上下文中定义的,而是在定义代码块的环境中定义。“闭包” 一词来源于以下两者的结合:要执行的代码块(由于自由变量的存在,相关变量引用没有释放)和为自由变量提供绑定的计算环境(作用域)。在 Scheme、Common Lisp、Smalltalk、Groovy、JavaScript、Ruby 和 Python 等语言中都能找到对闭包不同程度的支持。
闭包的价值
闭包的价值在于可以作为函数对象 或者匿名函数,对于类型系统而言这就意味着不仅要表示数据还要表示代码。支持闭包的多数语言都将函数作为第一级对象,就是说这些函数可以存储到变量中、作为参数传递给其他函数,最重要的是能够被函数动态地创建和返回。
闭包的本质
集合 <math>S<math> 是闭集当且仅当 <math>Cl(S)=S<math>。特别的,空集的闭包是空集,<math>X<math> 的闭包是 <math>X<math>。集合的交集的闭包总是集合的闭包的交集的子集(不一定是真子集)。有限多个集合的并集的闭包和这些集合的闭包的并集相等;零个集合的并集为空集,所以这个命题包含了前面的空集的闭包的特殊情况。无限多个集合的并集的闭包不一定等于这些集合的闭包的并集,但前者一定是后者的父集
若 <math>A<math> 为包含 <math>S<math> 的 <math>X<math> 的子空间,则 <math>S<math> 在 <math>A<math> 中计算得到的闭包等于 <math>A<math> 和 <math>S<math> 在 <math>X<math> 中计算得到的闭包(<math>Cl_A(S) = A/cap Cl_X(S)<math>)的交集。特别的,<math>S<math> 在 <math>A<math> 中是稠密的,当且仅当 <math>A<math> 是 <math>Cl_X(S)<math> 的子集。
闭包点
对欧几里德空间的子集 S,x 是 S 的闭包点,若所有以 x 为中心的开球都包含 S 的点(这个点也可以是 x)。
这个定义可以推广到度量空间 X 的任意子集 S。具体地说,对具有度量 d 的度量空间 X,x 是 S 的闭包点,若对所有 r > 0,存在 y 属于 S,使得距离 d(x, y) < r(同样的,可以是 x = y)。另一种说法可以是,x 是 S 的闭包点,若距离 d(x, S) := inf{d(x, s) : s 属于 S} = 0(这里 inf 表示下确界)。
这个定义也可以推广到拓扑空间,只需要用邻域替代“开球”。设 S 是拓扑空间 X 的子集,则 x 是 S 的闭包点,若所有 x 邻域都包含 S 的点。注意,这个定义并不要求邻域是开的。
极限点
闭包点的定义非常接近极限点的定义。这两个定义之间的差别非常微小但很重要——在极限点的定义中,点 x 的邻域必须包含和 x 不同的集合的点。
因此,所有极限点都是闭包点,但不是所有的闭包点都是极限点。不是极限点的闭包点就是孤点。也就是说,点 x 是孤点,若它是 S 的元素,且存在 x 的邻域,该邻域中除了 x 没有其他的点属于 S。
对给定的集合 S 和点 x,x 是 S 的闭包点,当且仅当 x 属于 S,或 x 是 S 的极限点。
集合的闭包
集合 S 的闭包是所有 S 的闭包点组成的集合。S 的闭包写作 cl(S),Cl(S) 或 S−。集合的闭包具有如下性质:
cl(S) 是 S 的闭父集。
cl(S) 是所有包含 S 的闭集的交集。
cl(S) 是包含 S 的最小的闭集。
集合 S 是闭集,当且仅当 S = cl(S)。
若 S 是 T 的子集,则 cl(S) 是 cl(T) 的子集。
若 A 是闭集,则 A 包含 S 当且仅当 A 包含 cl(S)。
有时候,上述第二或第三条性质会被作为拓扑闭包的定义。
在第一可数空间(如度量空间)中,cl(S) 是所有点的收敛数列的所有极限。
Perl中的闭包
闭包 (closure)是个精确但又很难解释的电脑名词。在 Perl 里面,闭包是以 匿名函数的形式来实现,具有持续参照位于该函数范围之外的文字式变数值的能力。 这些外部的文字变数会神奇地保留它们在闭包函数最初定义时的值 (深连结)。
如果一个程式语言容许函数递回另一个函数的话 (像 Perl 就是),闭包便具有意 义。要注意的是,有些语言虽提供匿名函数的功能,但却无法正确处理闭包; Python 这个语言便是一例。如果要想多了解闭包的话,建议你去找本功能性程式 设计的教科书来看。Scheme这个语言不仅支援闭包,更鼓励多加使用。
以下是个典型的产生函数的函数:
sub add_function_generator {
return sub { shift + shift };
}
$add_sub = add_function_generator();
$sum = &$add_sub(4,5); # $sum现在是 9了
闭包用起来就像是个函数样板,其中保留了一些可以在稍後再填入的空格。 add_function_generator() 所递回的匿名函数在技术上来讲并不能算是一个闭包, 因为它没有用到任何位在这个函数范围之外的文字变数。
把上面这个例子和下面这个make_adder()函数对照一下,下面这个函数所递回的匿名函数中使用了一个外部的文字变数。这种指名外部函数的作法需要由 Perl递回一个适当的闭包,因此那个文字变数在匿名函数产生之时的值便永久地被锁进闭 包里。
sub make_adder {
my $addpiece = shift;
return sub { shift + $addpiece };
}
$f1 = make_adder(20);
$f2 = make_adder(555);
这样一来&$f1($n) 永远会是 20加上你传进去的值$n ,而&$f2($n) 将 永远会是 555加上你传进去的值$n。$addpiece的值会在闭包中保留下来。
闭包在比较实际的场合中也常用得到,譬如当你想把一些程式码传入一个函数时:
my $line;
timeout( 30, sub { $line = <STDIN> } );
如果要执行的程式码当初是以字串的形式传入的话,即'$line = <STDIN>' ,那么timeout() 这个假想的函数在回到该函数被呼叫时所在的范围後便无法再撷取$list这个文字变数的值了。
在Javascript中闭包(Closure)
闭包的两个特点:
1、作为一个函数变量的一个引用 - 当函数返回时,其处于激活状态。
2、一个闭包就是当一个函数返回时,一个没有释放资源的栈区。
例1。
<script type="text/javascript">
function sayHello2(name) {
var text = 'Hello ' + name; // local variable
var sayAlert = function() { alert(text); }
return sayAlert;
}
var sy = sayHello2('never-online');
sy();
</script>
在Python中的闭包(Closure)
学过Java GUI编程的人都知道定义匿名内部类是注册监听等处理的简洁有效手段,闭包的定义方式有点类似于这种匿名内部类,
但是闭包的作用威力远远超过匿名内部类,这也是很多流行动态语言选择闭包的原因,相信你在JavaScript中已经了解它的神奇功效了。
定义:如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure)。
简单闭包的例子:
下面是一个使用闭包简单的例子,模拟一个计数器,通过将整型包裹为一个列表的单一元素来模拟使看起来更易变:
函数counter()所作的唯一一件事就是接受一个初始化的值来计数,并将该值赋给列表count成员,然后定义一个内部函数incr()。通过内部函数使用变量count,就创建了一个闭包。最魔法的地方是counter()函数返回一个incr(),一个可以调用的函数对象。
运行:
>>> c = counter(5)
>>> type(c)
<type 'function'>
>>> print c()
6
>>> print c()
7
>>> c2 = counter(99)
100
>>> print c()
8
在离散中“关系”的闭包(Closure)
离散中,一个关系R的闭包,是指加上最小数目的有序偶而形成的具有自反性,对称性或传递性的新的有序偶集,此集就是关系R的闭包