我们在开发中经常听到闭包这个概念,为什么要有闭包的概念,我总是感觉闭包这个概念很鸡肋,那么最初设计闭包的人是怎么想的。
为什么要有闭包?
闭包的定义
首先闭包有概念之后才能问上面的问题,先给闭包一个概念。
在计算机科学中,闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是引用了自由变量的函数。
上面是维基百科的定义,定义比较简单,首先闭包是个函数,其次就是引用了一个字变量,好像问题没有这样简单。
class A {
int sum = 0;
int sum(int a) {
return a = a + sum;
}
}
按照上面的说明,java中sum这个函数就是闭包的。是维基百科的概念是否有问题,还是我的java世界崩塌了。
另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。
这个定义是闭包的概念、形式与应用文章中的定义,看上去没有什么大的区别,但是这里强调的是一个引用环境的整体。看上去还是很难理解,我们先放下概念,回头看能不能自己给出属于自己的定义。
闭包的由来
计算机的目的就是计算,也可以这样狭隘的理解,计算机要能完成可计算公式的计算和逻辑。要完成这样的事情,需要一套东西去支持。
阿隆佐邱奇(Alonzo Church)发明了Lambda演算,也就是λ演算。
Lambda演算
λ演算(英语:lambda calculus,λ-calculus)是一套从数学逻辑中发展,以变量绑定和替换的规则,来研究函数如何抽象化定义、函数如何被应用以及递归的形式系统。
因为这里Lambda演算不是讨论的重点,我举几个简单的例子,让大家明白就好。
在Lambda演算中,每个表达式都代表一个函数,这个函数有一个参数,并且会返回一个值。也就是说在Lambda演算中只有函数。
-
Lambda演算的基本定义:
λx. E,x是参数,并且有且仅有一个参数,E是函数体。
-
函数的应用:
E1 E2,E1是个函数,E2也是个函数,并且每个函数都有返回值,E2的返回值当成λx. E的x,带人到E1的函数中,在返回E1的结果。
这样还是很难理解,再来个个例子。
现在我们定义一个数学函数f(x) = x + 2,数学意义很明显,就给x加上2。怎么用Lambda演算弄?
-
Lambda演算的基本定义:
λx. x + 2,x是参数,x + 2函数体。
-
函数的应用:
当有个参数λa.a 3的时候就是这个样子(λx. x + 2) (λa.a 3),可以写成也就符合上面的E1 E2格式。结果就是(λx. x + 2)3 = 3 + 2 = 5
这里有没有特别的熟悉,说说编程函数定义。
returnValue funcitonName(parameter){
methodBody
}
假如这个函数只能传入一个参数,那么是不是就是 λx. E的简单表达,原来函数的由来可以这样追溯。
Lambda演算的加法问题
Lambda演算只支持一个参数,我想计算f(x, y) = x + y怎么算呢,λx y. x + y,这样是违反规则的,不要着急我们可以采用Currying的方式,λx.λy.x + y,调用的时候是这样的(λx.λy.x + y) (7 2) = (λy.7 + y)(2) = (7 + 2) = 9。想一下程序怎样写。
function add(x){
return (function(y) {
return x+y;
});
}
add(100)(12);
这个不是闭包吗,我们绕了一大圈终于把闭包的源头找到了,闭包是这样来的。
现在我们尝试一下给闭包下个定义,首先闭包应该是函数内套用函数,这样我的java世界会来了,因为java是类里面有函数,函数是不能套用函数的(语法是这样的)。
这是尝试下个定义,函数本身被当做参数传入函数中,并且函数直接有特殊的作用域。
其它推荐
参考
- 我的最爱Lambda演算,一篇关于Lambda演算的文章。
- 维基百科-闭包
- 闭包的概念、形式与应用,来自IBM的文章,很不错。
- 符号: 抽象、语义,从符号的角度介绍了Lambda演算。