日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

闭包漫谈(从抽象代数及函数式编程角度)

發(fā)布時(shí)間:2025/3/21 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 闭包漫谈(从抽象代数及函数式编程角度) 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

如果Google一下“閉包”這個(gè)詞,會(huì)發(fā)現(xiàn)網(wǎng)上關(guān)于閉包的文章已經(jīng)不計(jì)其數(shù),甚至很多人將閉包看做面試JavaScript程序員的必考題(雖然閉包和JavaScript沒(méi)有什么必然聯(lián)系)。既然如此,我為什么還要寫(xiě)一篇關(guān)于閉包的文章呢?

首先,雖然網(wǎng)上關(guān)于閉包的文章甚多,但是很少以較為形式化的角度闡述閉包,而我認(rèn)為理解閉包的關(guān)鍵之一就是從形式化角度理解其涵義;其次,大多數(shù)文章將閉包的概念與JavaScript語(yǔ)言綁定太死,這樣容易局限對(duì)閉包概念的理解,而難以窺探到其本質(zhì)。從JavaScript去理解閉包,個(gè)人認(rèn)為這是本末倒置的,應(yīng)該先理解純粹意義上的閉包,然后再理解JavaScript中的閉包機(jī)制。

基于以上情況,本文將從較為形式化的角度闡述閉包概念,當(dāng)做對(duì)現(xiàn)有網(wǎng)絡(luò)的資料的一個(gè)補(bǔ)充。

一個(gè)需要明確的重要事實(shí)

在開(kāi)始闡述閉包之前,我需要特別明確一個(gè)非常重要的事實(shí),那就是“閉包(closure)”一詞被用于定義兩個(gè)毫不相關(guān)的概念,分別是數(shù)學(xué)領(lǐng)域抽象代數(shù)分支下用于描述集合之于運(yùn)算的一種性質(zhì)以及計(jì)算機(jī)科學(xué)程序設(shè)計(jì)語(yǔ)言分支用于描述函數(shù)式語(yǔ)言所支持的一種機(jī)制。這種不同大約就如“電影《人猿泰山》”和“五岳之尊泰山”中的“泰山”差不多,兩個(gè)短語(yǔ)中的“泰山”描述的是兩個(gè)風(fēng)馬牛不相及的概念,雖然是同一個(gè)詞。

一般來(lái)說(shuō),作為程序員我們說(shuō)的閉包更多是指后者,但是如果你與我一樣同時(shí)具有一點(diǎn)數(shù)學(xué)背景,第一次接觸“閉包”一詞是在抽象數(shù)學(xué)中的話,那么當(dāng)剛接觸到計(jì)算機(jī)中的“閉包”時(shí)多少會(huì)產(chǎn)生困惑,同樣,如果你是一個(gè)純粹的程序員,那么當(dāng)在數(shù)學(xué)著作中讀到“閉包”一詞時(shí)請(qǐng)小心區(qū)分這個(gè)“閉包”具體是表述哪一個(gè)概念。

我會(huì)在下文中分別闡述數(shù)學(xué)領(lǐng)域和計(jì)算機(jī)科學(xué)領(lǐng)域閉包的概念。

抽象代數(shù)中的閉包

抽象代數(shù)是一門(mén)研究代數(shù)結(jié)構(gòu)的數(shù)學(xué)分支,它的研究對(duì)象包括群、環(huán)、域和向量空間等等。當(dāng)然我在這里絲毫沒(méi)有要大談特談這些令人頭大的概念的意思,我會(huì)盡量以一種易懂的半形式化方式去闡述一些概念。

集合的定義

非正式地,集合是N個(gè)對(duì)象組成的一個(gè)無(wú)序、互異且確定的整體。其中N是自然數(shù)。這些對(duì)象稱(chēng)為集合的元素。

無(wú)序性是指集合中的元素不存在序關(guān)系(集合上可以定義序關(guān)系,但這些序關(guān)系不是集合本身的一部分),每個(gè)元素的地位是相同的。

互異性是指集合中任意兩個(gè)元素是不同的,即同一集合中任意兩個(gè)元素間不存在等價(jià)關(guān)系。

確定是指對(duì)任意一個(gè)對(duì)象和任意一個(gè)集合,這個(gè)對(duì)象要么屬于此集合,要么不屬于此集合,二者必居其一,不存在模棱兩可的狀態(tài)(模糊數(shù)學(xué)中有一種中間隸屬狀態(tài),本文不考慮模糊數(shù)學(xué)領(lǐng)域)。

運(yùn)算的定義

非正式地,集合上的n元運(yùn)算是一個(gè)映射,這個(gè)映射將作用于任意n個(gè)集合中的元素,并映射為一個(gè)結(jié)果(注意結(jié)果不一定屬于這個(gè)集合)。

例如,設(shè)N+N+是正整數(shù)集合,那么加法(++)和減法(??)都可以看做定義在N+N+上的二元運(yùn)算,因?yàn)槿我鈨蓚€(gè)正整數(shù)都可以進(jìn)行加減。

封閉的定義

有了集合和運(yùn)算的概念,就可以定義封閉的概念了。

非正式地,如果定義于集合AA上的運(yùn)算的運(yùn)算結(jié)果仍然屬于AA,那么運(yùn)算對(duì)于集合AA是封閉的。下面給出“封閉”的一個(gè)半形式化定義:

如果對(duì)于任意a1,a2Aa1,a2∈A,有a1a2Aa1⊕a2∈A,那么說(shuō)二元運(yùn)算對(duì)于集合A是封閉的。

例如“++”對(duì)于N+N+是封閉的,因?yàn)槿我鈨蓚€(gè)正整數(shù)的和結(jié)果仍然是正整數(shù);但是“??”對(duì)于N+N+不是封閉的,例如3和5屬于N+N+,但是:3?5=?2?N+3?5=?2?N+

閉包性質(zhì)

一個(gè)集合滿足閉包性質(zhì)當(dāng)且僅當(dāng)這個(gè)集合在某個(gè)運(yùn)算或某些運(yùn)算的搜集下是封閉的,其中“某些運(yùn)算的搜集下封閉”是指這個(gè)集合單獨(dú)閉合在每個(gè)運(yùn)算之下。

值得一提的是,之前這條定義往往被作為一條公理引入一個(gè)代數(shù)結(jié)構(gòu),叫做“閉包公理”。但是現(xiàn)代集合論往往將運(yùn)算形式化的定義為集合間的運(yùn)算,所以將其作為公理引入代數(shù)結(jié)構(gòu)是多余的(因?yàn)榭梢酝ㄟ^(guò)其它公理間接定義閉包公理),但是對(duì)于子集是否閉合的問(wèn)題,閉包公理仍然有意義。

一個(gè)例子 - 存在于形式語(yǔ)言與自動(dòng)機(jī)理論中的閉包

上面說(shuō)了很多東西,我們來(lái)看一個(gè)抽象代數(shù)領(lǐng)域閉包的例子。

我們回想在形式語(yǔ)言與自動(dòng)機(jī)理論中(或者編譯原理中也有提到)在定義語(yǔ)言時(shí)做的一些推導(dǎo)。

一般我們會(huì)先定義一個(gè)有限集合ΣΣ,叫做字母表,ΣΣ的n階冪運(yùn)算定義為形成一個(gè)新的集合ΣnΣn,一個(gè)對(duì)象屬于ΣnΣn當(dāng)且僅當(dāng)它是ΣΣ中任意n個(gè)字母連接成的串,其中Σ0={ε}Σ0={ε}。而

Σ?=Σ0Σ1...Σn...Σ?=Σ0∪Σ1∪...∪Σn∪...

此時(shí)集合Σ?Σ?滿足閉包性質(zhì),因?yàn)檫@個(gè)集合對(duì)于冪運(yùn)算是封閉的。有興趣的讀者可以自行證明一下。

函數(shù)式編程中的閉包

在這一章節(jié)開(kāi)始之前,我需要再和大家明確一個(gè)比較糾結(jié)的事實(shí),就是在函數(shù)式編程領(lǐng)域中當(dāng)說(shuō)到“閉包”時(shí),也有可能是指數(shù)學(xué)領(lǐng)域中閉包的概念,這是因?yàn)楹瘮?shù)式編程在基礎(chǔ)理論與抽象代數(shù)有一定親緣性,所以當(dāng)在函數(shù)式語(yǔ)言著作中討論“閉包”時(shí),有可能是在抽象數(shù)學(xué)的上下文中討論的。然而,在表述上可能會(huì)有微妙變化。在函數(shù)式語(yǔ)言領(lǐng)域?qū)τ跀?shù)學(xué)閉包常用的表述是“如果一個(gè)運(yùn)算的結(jié)果仍然能被此運(yùn)算作用,則這個(gè)運(yùn)算是封閉的”,要注意這只不過(guò)是上文提到的“閉包”概念的另一種等價(jià)表述而已,如果我們將這個(gè)運(yùn)算的所有結(jié)果看做一個(gè)集合,那么就可以等價(jià)表述說(shuō)這個(gè)運(yùn)算在這個(gè)集合上是封閉的。

而我下面所要闡述的閉包是一種截然不同的概念。所以,當(dāng)在函數(shù)式語(yǔ)言的著作中看到“閉包”時(shí),需要根據(jù)上下文環(huán)境小心區(qū)分其表述哪種概念。

Lambda演算與自由變量

函數(shù)式編程語(yǔ)言的基礎(chǔ)是lambda演算,這是一套用于研究函數(shù)定義、應(yīng)用和遞歸的形式系統(tǒng),由數(shù)學(xué)家丘奇在20世紀(jì)30年代引入。如果您不太熟悉lambda演算,那么維基百科相關(guān)頁(yè)面是很好的快速入門(mén)資料,請(qǐng)?jiān)徫也粫?huì)完整描述lambda演算(因?yàn)橐呀?jīng)有很多可以參考的資料)。

簡(jiǎn)單來(lái)說(shuō)lambda演算將計(jì)算過(guò)程看過(guò)一系列的函數(shù)代換,例如,下面是加運(yùn)算的lambda函數(shù)(假設(shè)+運(yùn)算已經(jīng)定義):

λx.λy.x+yλx.λy.x+y

lambda演算就是反復(fù)將函數(shù)應(yīng)用于實(shí)際值,并用實(shí)際值代替參數(shù),最終得出結(jié)果。例如下面是7+2的計(jì)算過(guò)程:

(λx.λy.x+y)72?(λy.7+y)2?7+2?9(λx.λy.x+y)72?(λy.7+y)2?7+2?9

首先用第一個(gè)參數(shù)(7)代替最外層函數(shù)的參數(shù)(x),然后用第二個(gè)參數(shù)(2)代替第二層函數(shù)的參數(shù)(y),最終得到計(jì)算結(jié)果。

鑒于如果下面大量使用lambda演算描述問(wèn)題大家可能會(huì)崩潰(我也會(huì)崩潰),我將改用函數(shù)式語(yǔ)言scheme(Lisp的一個(gè)方言)來(lái)進(jìn)行問(wèn)題描述。注意其實(shí)scheme在本質(zhì)上與lambda演算是等價(jià)的,只是看起來(lái)更好懂,例如不需要遵循lambda演算一個(gè)變態(tài)的規(guī)定:每個(gè)函數(shù)只允許有一個(gè)參數(shù)(雖然任何多參數(shù)函數(shù)式程序都可以通過(guò)Currying過(guò)程化歸為等價(jià)的lambda演算)。

下面是用scheme程序?qū)ι鲜鰈ambda演算的等價(jià)表示:

  • (define (f x y) (+ x y))
  • 可以這樣計(jì)算7+2:

  • (f 7 2)
  • ;Value: 9
  • 下面看一個(gè)稍微復(fù)雜點(diǎn)的例子:

  • (define (f x) (lambda (y) (+ x y)))
  • 這里定義了函數(shù)f,接受一個(gè)參數(shù)x,特別要注意它的返回值:不是一個(gè)值而是一個(gè)匿名函數(shù)。如果我們把這個(gè)函數(shù)單獨(dú)拿出來(lái):

  • (lambda (y) (+ x y))
  • 可以看到,這個(gè)匿名函數(shù)接收一個(gè)參數(shù)y,但是卻沒(méi)有參數(shù)x!也就是說(shuō),如果脫離上下文執(zhí)行這個(gè)函數(shù),則x處于未指定狀態(tài),我們說(shuō)對(duì)于這個(gè)函數(shù),y是綁定的,而x是自由的。

    一般地:x是一個(gè)函數(shù)的函數(shù)體中的變量,如果x被這個(gè)函數(shù)的參數(shù)指定,則x是綁定于這個(gè)函數(shù)的,否則說(shuō)x對(duì)于此函數(shù)是自由的。

    下面可以看到,變量的綁定和自由概念是理解閉包本質(zhì)的一把鑰匙。

    程序語(yǔ)言的閉包性質(zhì)

    繼續(xù)上面的scheme程序,我們已經(jīng)定義了函數(shù)f:

  • (define (f x) (lambda (y) (+ x y)))
  • 如果我們運(yùn)行下面程序:

  • (f 7)
  • ;Value 13: #[compound-procedure 13]
  • 可以看到,f返回了一個(gè)過(guò)程(匿名函數(shù)),按照函數(shù)演算規(guī)則,這個(gè)函數(shù)應(yīng)該是:

  • (lambda (y) (+ 7 y))
  • 那么下面的運(yùn)算就很直觀了:

  • ((f 7) 2)
  • ;Value: 9
  • 注意這里有一個(gè)非常重要的地方(也是閉包性質(zhì)的關(guān)鍵),那就是這個(gè)運(yùn)算執(zhí)行了兩個(gè)函數(shù):f和匿名函數(shù)。而f的作用域?yàn)?f 7),這就是說(shuō),其實(shí)在(f 7)之后,f這個(gè)函數(shù)就結(jié)束了,而x(這里被賦值為7)是f的私有變量(綁定于f),那么程序設(shè)計(jì)語(yǔ)言的設(shè)計(jì)者就有兩種選擇:第一,在函數(shù)超出其作用域后立即銷(xiāo)毀其綁定變量,如果是這樣的話,((f 7) 2) 是無(wú)法得出結(jié)果的,因?yàn)樵谕鈱拥膄運(yùn)算結(jié)束后,存放數(shù)值“7”的變量就被釋放了,所以匿名函數(shù)無(wú)法得到其自由變量x的值;顯然scheme的設(shè)計(jì)者做了第二種選擇:如果一個(gè)函數(shù)返回另一個(gè)函數(shù),而被返回函數(shù)又需要外層函數(shù)的變量時(shí),不會(huì)立即釋放這個(gè)變量,而是允許被返回的函數(shù)引用這些變量。支持這種機(jī)制的語(yǔ)言稱(chēng)為支持閉包機(jī)制,而這個(gè)內(nèi)部函數(shù)連同其自由變量就形成了一個(gè)閉包。

    上面的這段話不太好理解,但是請(qǐng)務(wù)必多讀幾遍,因?yàn)?#xff0c;這就是閉包的全部。

    從Scheme到JavaScript

    好的,現(xiàn)在開(kāi)始討論JavaScript中的閉包。

    上文說(shuō)過(guò),閉包是函數(shù)式語(yǔ)言才有的機(jī)制,或者說(shuō)支持函數(shù)式編程泛型的語(yǔ)言才有的性質(zhì)。一個(gè)語(yǔ)言支持函數(shù)式編程泛型,如果它同時(shí)具有下列特性:

    可以將一個(gè)函數(shù)賦值給一個(gè)變量。

    函數(shù)可以作為參數(shù)傳遞給另一個(gè)函數(shù)。

    函數(shù)的返回值可以是一個(gè)函數(shù)。

    結(jié)合上面關(guān)于閉包性質(zhì)的定義,就很清楚為什么只有支持函數(shù)編程泛型的語(yǔ)言才可以談閉包性質(zhì)。

    很顯然,JavaScript是具有上述三條性質(zhì)的,所以可以說(shuō)JavaScript擁有函數(shù)式編程泛型。當(dāng)然,一般我們還是習(xí)慣于用命令式的寫(xiě)JavaScript,但是其閉包本質(zhì)是一樣的。為了說(shuō)明這一點(diǎn),我將會(huì)首先用JavaScript按照函數(shù)式泛型重寫(xiě)上面的scheme程序,然后轉(zhuǎn)為命令式。

    上文用scheme所寫(xiě)的函數(shù)f,可以用JavaScript等價(jià)實(shí)現(xiàn)如下:

  • function f(x){ return function(y) { return x + y; }; }
  • 可以執(zhí)行與上述scheme類(lèi)似的計(jì)算(因?yàn)槊撾x了瀏覽器,我是用nodejs執(zhí)行這段JavaScript):

  • console.log( (f(7)) (2) );
  • //9
  • 其中f返回的匿名函數(shù)與其自由變量x組成了一個(gè)閉包系統(tǒng)。

    如果用命令式重寫(xiě)上面的程序,就得到了我們熟悉的閉包:

  • function f(x) {
  • return function(y) {
  • return x + y;
  • };
  • }
  • ?
  • var lam = f(7);
  • console.log(lam(2));
  • 總結(jié)

    本文分別討論了抽象代數(shù)和函數(shù)式編程中兩個(gè)截然不同的閉包概念,當(dāng)然,作為程序員我們更關(guān)心的是后者(但前者也不是對(duì)程序員一點(diǎn)用也沒(méi)有,如果學(xué)習(xí)函數(shù)式語(yǔ)言的構(gòu)造原理,抽象代數(shù)中的閉包也是必須理解的概念)。實(shí)際上,閉包遠(yuǎn)沒(méi)有很多人想象的復(fù)雜和神秘,只不過(guò)是函數(shù)式編程中一個(gè)普通的概念,但是可能因?yàn)槲覀兇蠖鄶?shù)人是從命令式編程語(yǔ)言學(xué)習(xí)編程,很少去寫(xiě)函數(shù)式程序,而要理解閉包卻是需要結(jié)合函數(shù)式編程泛型,因此很難看清閉包的本質(zhì)。希望本文能對(duì)您有所幫助,同時(shí)我個(gè)人也建議可以學(xué)習(xí)一門(mén)函數(shù)式語(yǔ)言,這樣對(duì)很多概念的理解非常有好處。

    from:?http://blog.codinglabs.org/articles/closure-perspective-of-abstract-mathematic-and-functional-language.html

    總結(jié)

    以上是生活随笔為你收集整理的闭包漫谈(从抽象代数及函数式编程角度)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。