闭包 (计算机科学)

计算机科学中,闭包是一个有自己环境的函数。在这个环境中,至少有一个绑定的变量(一个有值的名字,比如数字)。闭包的环境在闭包的两次使用之间将绑定的变量保存在内存中。

Peter J. Landin在1964年将这个想法命名为闭包。1975年后,Scheme编程语言使闭包变得流行。在那之后,许多编程语言都有闭包。

匿名函数(没有名字的函数)有时被错误地称为闭包。大多数有匿名函数的语言也有闭包。如果一个匿名函数有一个自己的环境,并且至少有一个绑定的变量,那么它也是一个闭包。一个没有自己的环境的匿名函数不是闭包。一个命名的闭包不是匿名的。

闭包和第一类函数

可以是数字或其他类型的数据,如字母,或由较简单部分组成的数据结构。在编程语言的规则中,一流的值是可以给函数、由函数返回、并与变量名绑定的值。接受或返回其他函数的函数被称为高阶函数。大多数将函数作为第一类值的语言也有高阶函数和闭包。

例如,看一下下面的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; }; }

闭包环境在包围函数(导数)返回后保留了绑定的变量fdx。在没有闭包的语言中,这些值会在包围函数返回后丢失。在有闭包的语言中,只要任何闭包有绑定变量,就必须将其保留在内存中。

闭包不需要使用匿名函数来形成。例如,Python编程语言对匿名函数的支持有限,但确实有闭包。例如,上述ECMAScript的例子在Python中可以实现的一种方式是。

# 返回一个近似于f的导数的函数 # 使用一个dx的区间,这个区间应该适当地小。 def derivative(f, dx): def gradient(x): return (f(x + dx) - f(x))/ dx 返回梯度

在这个例子中,名为梯度的函数与变量fdx一起构成一个闭合。外围的导数函数返回这个闭包。在这种情况下,一个匿名函数也可以工作。

def derivative(f, dx): return lambda x: (f(x + dx) - f(x))/ dx

Python 必须经常使用命名函数来代替,因为它的 lambda 表达式只能包含其他表达式(返回一个值的代码),而不是语句(有效果但没有值的代码)。但是在其他语言中,比如Scheme,所有的代码都会返回一个值;在Scheme中,所有的东西都是一个表达式。

封闭的用途

封闭器有许多用途。

  • 软件库的设计者可以允许用户通过传递闭包作为重要函数的参数来定制行为。例如,一个对数值进行排序的函数可以接受一个闭包参数,根据用户定义的标准对要排序的数值进行比较。
  • 因为闭包会延迟评估--也就是说,它们在被调用之前不会 "做 "任何事情,所以它们可以用来定义控制结构。例如,所有Smalltalk的标准控制结构,包括分支(if/then/else)和循环(while和for),都是用方法接受闭包的对象定义的。用户也可以轻松地定义自己的控制结构。
  • 可以产生多个函数,它们在同一环境中关闭,使它们能够通过改变该环境(在允许分配的语言中)进行私下交流。

在计划中

(define foo #f) (define bar #f) (let ((secret-message "none")) (set! foo (lambda (msg) (set! secret-message msg))) (set! bar (lambda () secret-message)) (display (bar)) ; prints "none" (newline) (foo "meet me by docks at midnight" ) (display (bar)) ; prints "meet me by docks at midnight"
  • 闭包可以用来实现对象系统。

注意:有些演讲者把任何绑定词性环境的数据结构称为闭包,但这个术语通常特指函数。

问题和答案

问:什么是计算机科学中的封闭?
答:闭包是一个有自己的环境的函数。

问:闭包的环境包含什么?
答:闭包的环境至少包含一个约束变量。

问:谁给闭包的概念起的名字?
答:Peter J. Landin在1964年给闭包的概念起了个名字。

问:哪种编程语言在1975年后使闭包变得流行?
答:1975年后,Scheme编程语言使闭包变得流行。

问:匿名函数和闭包是一回事吗?
答:匿名函数有时被错误地称为闭包,但并非所有匿名函数都是闭包。

问:是什么使匿名函数成为闭包?
答:如果一个匿名函数有自己的环境,至少有一个绑定的变量,那么它就是一个闭包。

问:一个命名的闭包是匿名的吗?
答:不是,一个命名的闭包不是匿名的。

AlegsaOnline.com - 2020 / 2023 - License CC3