值可以是数字或其他类型的数据,如字母,或由较简单部分组成的数据结构。在编程语言的规则中,一流的值是可以给函数、由函数返回、并与变量名绑定的值。接受或返回其他函数的函数被称为高阶函数。大多数将函数作为第一类值的语言也有高阶函数和闭包。
例如,看一下下面的Scheme函数。
; 返回所有至少售出THRESHOLD份数的书的列表。(define (best-selling-books threshold) (filter (lambda (book) (>= (book-sales book) threshold) ) book-list))
在这个例子中,lambda表达式(lambda (book) (>= (book-sales book) threshold))是函数best-selling-books的一部分。当函数运行时,Scheme必须生成lambda的值。它的做法是用lambda的代码和对阈值变量的引用建立一个闭包,阈值是lambda中的一个自由变量。(自由变量是一个没有与值绑定的名称)。
然后,过滤器函数在列表中的每本书上运行闭包,以挑选要返回的书。因为闭包本身有一个对阈值的引用,闭包可以在每次 filter 运行闭包时使用这个值。函数过滤器本身可以写在一个完全独立的文件中。
下面是用ECMAScript(JavaScript)重写的同一个例子,这是另一种支持闭包的流行语言。
// 返回至少有'阈值'销量的所有书籍的列表。 function bestSellingBooks(阈值) { return bookList. filter( function(book) { return book. sales >= threshold; } ); }
ECMAScript在这里用function这个词来代替lambda,用Array.filter方法来代替filter函数,但除此之外,代码以同样的方式做同样的事情。
一个函数可以创建一个闭包并返回它。下面的例子是一个函数,它返回一个函数。
在计划中。
; 返回一个近似于f的导数的函数; 使用一个dx的区间,它应该是适当的小。 (定义(导数f dx) (lambda (x) (/ (- (f (+ x dx))(f x)) dx)))
在ECMAScript:
// 返回一个近似于f的导数的函数 // 使用一个dx的区间,这个区间应该适当地小。 function derivative(f, dx) { return function(x) { return (f(x + dx) - f(x))/ dx; }; }
闭包环境在包围函数(导数)返回后保留了绑定的变量f和dx。在没有闭包的语言中,这些值会在包围函数返回后丢失。在有闭包的语言中,只要任何闭包有绑定变量,就必须将其保留在内存中。
闭包不需要使用匿名函数来形成。例如,Python编程语言对匿名函数的支持有限,但确实有闭包。例如,上述ECMAScript的例子在Python中可以实现的一种方式是。
# 返回一个近似于f的导数的函数 # 使用一个dx的区间,这个区间应该适当地小。 def derivative(f, dx): def gradient(x): return (f(x + dx) - f(x))/ dx 返回梯度
在这个例子中,名为梯度的函数与变量f和dx一起构成一个闭合。外围的导数函数返回这个闭包。在这种情况下,一个匿名函数也可以工作。
def derivative(f, dx): return lambda x: (f(x + dx) - f(x))/ dx
Python 必须经常使用命名函数来代替,因为它的 lambda 表达式只能包含其他表达式(返回一个值的代码),而不是语句(有效果但没有值的代码)。但是在其他语言中,比如Scheme,所有的代码都会返回一个值;在Scheme中,所有的东西都是一个表达式。