易飞滔Todd | 次生进化

闭包

在做程序设计语言作业时,用racket语言自己实现了一个新的语言,还带上了闭包功能,感觉终于深刻理解了闭包是个什么东西,虽然以前在C#中也无数次用到匿名函数,接触了闭包的概念,但是换了一种语言来看,好像找到了这种概念的源头。

一个函数中有很多变量,有一些是函数中定义的(这包含在函数体中),有一些是作为参数传入的,因此一个普通的函数调用,需要知道函数体,也需要知道传入的参数,我们可以称这些参数是不自由的,已经由传入决定了。

如果一个函数中有些变量不是由传入参数决定的呢?那么这些参数是自由的,它们由定义函数的环境所决定。可以简单的认为,函数与定义函数的环境就称为一个闭包,在racket等语言中,实际上所有的函数在底层都会用闭包形式表达,这个闭包包含一个环境,环境提供变量与值的映射,还包括函数体代码,在一些实现中,可以把传入参数的映射也放大环境中,说到这里,发现闭包没什么神奇的,只不过是一个函数体,加上一个变量和值的映射表而已。

在函数式编程语言中,闭包往往很重要,闭包作为值,可以像整数、浮点数一样传递,这个传递的值,如果被另一个函数调用,就像你租了一个餐馆自己做菜,原料(函数调用方待处理的数据)由餐馆提供,但是你自己带了佐料(环境中的变量与值的映射),也带了处理原料的方法(函数体),这里的佐料和处理方法可以千变万化,调用的函数都不关心,因此有了极大的灵活性,比如经典的map函数需要一个处理函数作为参数,只规定了这个函数必须处理列表中的元素,至于怎么处理,是否带上其他复杂的参数,都由这个处理函数蕴含的闭包决定。

闭包还和匿名函数的概念联系起来,匿名函数都是在某个函数中定义并就地调用,匿名函数如果能使用相关环境中的变量,那么就是一个闭包。比如C#中的匿名函数,可以使用定义它的函数所在的对象的所有字段。实际上C#的匿名函数是用一个类实现的,这个类就包含了这个匿名函数(会生成一个名字),还有它所引用的调用方的字段作为私有字段。可以说,C#是用类实现了闭包。

闭包这个词,可以拆开来理解。

  1. “闭”代表封闭,闭包对修改是封闭的,闭包中的函数一般来说无从修改,不像对象中的函数,一般是可以通过子类来重载修改的,这不便于扩展,但更稳定。
  2. “包”正如前面描述的,代表是函数及定义该函数的环境的包裹。