闭包和状态表达
编辑
闭包可以用来在一个函数与一组“私有”变量之间建立关联关系。在给定函数被多次调用的过程中,这些私有变量能够保持其持久性。变量的作用域仅限于包含它们的函数,因此无法从其它程序代码部分进行访问。不过,变量的生存期是可以很长,在一次函数调用期间所建立所生成的值在下次函数调用时仍然存在。正因为这一特点,闭包可以用来完成信息隐藏,并进而应用于需要状态表达的某些编程范型中。
不过,用这种方式来使用闭包时,闭包不再具有参照透明性(英语:Referential transparency),因此也不再是纯函数。即便如此,在某些非纯函数式编程语言,例如Scheme中,闭包还是得到了广泛的使用。
闭包和头类函数
编辑
主条目:头等函数
典型的支持闭包的语言中,通常将函数当作头等函数——在这些语言中,函数可以被当作参数传递、也可以作为函数返回值、绑定到变量名、就像字符串、整数等简单类型。例如以下Scheme代码:
; Return a list of all books with at least THRESHOLD copies sold.
(define (best-selling-books threshold)
(filter
(lambda (book)
(>= (book-sales book) threshold))
book-list))
在这个例子中,lambda表达式(lambda (book) (>= (book-sales book) threshold))出现在函数best-selling-books中。当这个lambda表达式被执行时,Scheme创造了一个包含此表达式以及对threshold变量的引用的闭包,其中threshold变量在lambda表达式中是自由变量。
这个闭包接着被传递到filter函数。这个函数的功能是重复调用这个闭包以判断哪些书需要增加到列表哪些书需要丢弃。因为闭包中引用了变量threshold,所以它在每次被filter调用时都可以使用这个变量,虽然filter可能定义在另一个文件中。
下面是用ECMAScript (JavaScript)写的同一个例子:
// Return a list of all books with at least 'threshold' copies sold.
function bestSellingBooks(threshold) {
return bookList.filter(
function (book) { return book.sales >= threshold; }
);
}
这里,关键字function取代了lambda,Array.filter方法[6]取代了filter函数,但两段代码的功能是一样的。
一个函数可以创建一个闭包并返回它,如下述JavaScript例子:
// Return a function that approximates the derivative of f
// using an interval of dx, which should be appropriately small.
function derivative(f, dx) {
return function (x) {
return (f(x + dx) - f(x)) / dx;
};
}
因为在这个例子中闭包已经超出了创建它的函数的范围,所以变量f和dx将在函数derivative返回后继续存在。在没有闭包的语言中,变量的生命周期只限于创建它的环境。但在有闭包的语言中,只要有一个闭包引用了这个变量,它就会一直存在。清理不被任何函数引用的变量的工作通常由垃圾回收完成,但对于 C++ 这种没有垃圾收集(起码目前仍没有一个为语言本身所认可的)的语言而言也不是难事——通过一些细致而琐碎的步骤。