【#文档大全网# 导语】以下是®文档大全网的小编为您整理的《闭包_数学概念与函数式编程概念》,欢迎阅读!
闭包: 数学概念与函数式编程概念
搜索下“闭包〞这个词,会发现网上关于闭包的文章已经不计其数,甚至很多人将闭包看做面试javascript程序员的必考题〔虽然闭包和 javascript没有什么必然联系〕。虽然网上关于闭包的文章甚多,但是很少以较为形式化的角度阐述闭包,而从一个比拟形式化角度理解其涵义对其的理 解是非常重要的。大多数文章将闭包的概念与javascript语言绑定太死,这样容易局限对闭包概念的理解,而难以窥探到其本质。从 javascript去理解闭包,难以了解其本质意义。
在这里我们试图先理解纯粹意义上的闭包,然后再理解javascript中的闭包机制。
基于以上情况,本文将从较为形式化的角度阐述闭包概念,当做对现有资料的一个补充。一个需要明确的重要事实 在开场阐述闭包之前,我需要特别明确一个非常重要的事实,那就是“闭包〔closure〕〞一词被用于定义两个毫不相关的概念,分别是数学领域抽象代数 分支下用于描述集合之于运算的一种性质以及计算机科学程序设计语言分支用于描述函数式语言所支持的一种机制。这种不同大约就如“电影《人猿泰山》〞和“五 岳之尊泰山〞中的“泰山〞差不多,两个短语中的“泰山〞描述的是两个风马牛不相及的概念,虽然是同一个词。
一般来说,作为程序员我们 说的闭包更多是指后者,但是如果你与我一样同时具有一点数学背景,第一次接触“闭包〞一词是在抽象数学中的话,那么当刚接触到计算机中的“闭包〞时多少会 产生困惑,同样,如果你是一个纯粹的程序员,那么当在数学著作中读到“闭包〞一词时请小心区分这个“闭包〞具体是表述哪一个概念。 我会在下文中分别阐述数学领域和计算机科学领域闭包的概念。代数学中的闭包
抽象代数是一门研究代数结构的数学分支,它的研究对象包括群、环、域和向量空间等等。当然我在这里丝毫没有要大谈特谈这些令人头大的概念的意思,我会尽量以一种易懂的半形式化方式去阐述一些概念。集合的定义
非正式地,集合是N个对象组成的一个无序、互异且确定的整体。其中N是自然数。这些对象称为集合的元素。 无序性是指集合中的元素不存在序关系〔集合上可以定义序关系,但这些序关系不是集合本身的一局部〕,每个元素的地位是一样的。
互异性是指集合中任意两个元素是不同的,即同一集合中任意两个元素间不存在等价关系 。
确定是指对任意一个对象和任意一个集合,这个对象要么属于此集合,要么不属于此集合,二者必居其一,不存在模棱两可的状态。运算的定义
非正式地,集合上的n元运算是一个映射,这个映射将作用于任意n个集合中的元素,并映射为一个结果〔注意结果不一定属于这个集合〕。
例如,设N+是正整数集合,那么加法〔+〕和减法〔?〕都可以看做定义在N+上的二元运算,因为任意两个正整数都可以进展加减。封闭的定义
有了集合和运算的概念,就可以定义封闭的概念了。
非正式地,如果定义于集合A上的运算⊕的运算结果仍然属于A,那么运算⊕对于集合A是封闭的。下面给出“封闭〞的一个半形式化定义:
如果对于任意a1,a2∈A,有a1⊕a2∈A,那么说二元运算⊕对于集合A是封闭的。
例如“+〞对于N+是封闭的,因为任意两个正整数的和结果仍然是正整数;但是“?〞对于N+不是封闭的,例如3和5属于N+,但是:3?5=?2?N+。闭包性质
一个集合满足闭包性质当且仅当这个集合在某个运算或某些运算的搜集下是封闭的,其中“某些运算的搜集下封闭〞是指这个集合单独闭合在每个运算之下。 。
一个例子
- 存在于形式语言与自动机理论中的闭包
上面说了很多东西,我们来看一个抽象代数领域闭包的例子。
我们回想在形式语言与自动机理论中〔或者编译原理中也有提到〕在定义语言时做的一些推导。
一般我们会先定义一个有限集合Σ,叫做字母表,Σ的n阶幂运算定义为形成一个新的集合Σn,一个对象属于Σn当且仅当它是Σ中任意n个字母连接成的串,其中Σ0={ε}。而 Σ?=Σ0∪Σ1∪...∪Σn∪...
此时集合Σ?满足闭包性质,因为这个集合对于幂运算是封闭的。有兴趣的读者可以自行证明一下。函数式编程中的闭包
在这一章节开场之前,我需要再和大家明确一个比拟纠结的事实,就是在函数式编程领域中当说到“闭包〞时,
1 / 3
也有可能是指数学领域中闭包的概念,这是因为函 数式编程在根底理论与抽象代数有一定亲缘性,所以当在函数式语言著作中讨论“闭包〞时,有可能是在抽象数学的上下文中讨论的。
然而,在表 述上可能会有微妙变化。在函数式语言领域对于数学闭包常用的表述是“如果一个运算的结果仍然能被此运算作用,那么这个运算是封闭的〞,要注意这只不过是上文 提到的“闭包〞概念的另一种等价表述而已,如果我们将这个运算的所有结果看做一个集合,那么就可以等价表述说这个运算在这个集合上是封闭的。
而我下面所要阐述的闭包是一种截然不同的概念。所以,当在函数式语言的著作中看到“闭包〞时,需要根据上下文环境小心区分其表述哪种概念。Lambda演算与自由变量
函数式编程语言的根底是lambda演算,这是一套用于研究函数定义、应用和递归的形式系统,由数学家丘奇在20世纪30年代引入。如果您不太熟悉 lambda演算,那么维基百科相关页面是很好的快速入门资料,请原谅我不会完整描述lambda演算〔因为已经有很多可以参考的资料〕。
简单来说lambda演算将计算过程看过一系列的函数代换,例如,下面是加运算的lambda函数〔假设+运算已经定义〕:
lambda演算就是反复将函数应用于实际值,并用实际值代替参数,最终得出结果。例如下面是7+2的计算过程: 首先用第一个参数〔7〕代替最外层函数的参数〔x〕,然后用第二个参数〔2〕代替第二层函数的参数〔y〕,最终得到计算结果。
鉴于如果下面大量使用lambda演算描述问题大家可能会崩溃〔我也会崩溃〕,我将改用函数式语言scheme
〔Lisp的一个方言〕来进展问题描述。 注意其实scheme在本质上与lambda演算是等价的,只是看起来更好懂,例如不需要遵循lambda演算一个变态的规定:每个函数只允许有一个参数 〔虽然任何多参数函数式程序都可以通过Currying过程化归为等价的lambda演算〕。
下面是用scheme程序对上述lambda演算的等价表示: (define (f x y) (+ x y))
可以这样计算7+2: (f 7 2);value: 9
下面看一个稍微复杂点的例子:
(define (f x) (lambda (y) (+ x y)))
这里定义了函数f,承受一个参数x,特别要注意它的返回值:不是一个值而是一个匿名函数。如果我们把这个函数单独拿出来:
(lambda (y) (+ x y))
可以看到,这个匿名函数接收一个参数y,但是却没有参数x!也就是说,如果脱离上下文执行这个函数,那么x处于未指定状态,我们说对于这个函数,y是绑定的,而x是自由的。
一般地:x是一个函数的函数体中的变量,如果x被这个函数的参数指定,那么x是绑定于这个函数的,否那么说x对于此函数是自由的。
下面可以看到,变量的绑定和自由概念是理解闭包本质的一把钥匙。程序语言的闭包性质 继续上面的scheme程序,我们已经定义了函数f: (define (f x) (lambda (y) (+ x y))) 如果我们运行下面程序:
(f 7);value 13: #[compound-procedure 13]
可以看到,f返回了一个过程〔匿名函数〕,按照函数演算规那么,这个函数应该是: (lambda (y) (+ 7 y)) 那么下面的运算就很直观了:
((f 7) 2);value: 9
注意这里有一个非常重要的地方〔也是闭包性质的关键〕,那就是这个运算执行了两个函数:f和匿名函数。而f的作用域为(f 7),这就是说,其实在(f 7)之后,f这个函数就完毕了,而x〔这里被赋值为7〕是f的私有变量〔绑定于f〕,那么程序设计语言的设计者就有两种选择:
第一,在函数超出其作用域后立即销毁其绑定变量,如果是这样的话,((f 7) 2) 是无法得出结果的,因为在外
2 / 3
本文来源:https://www.wddqxz.cn/48cb44a2ca50ad02de80d4d8d15abe23492f0303.html