多多色-多人伦交性欧美在线观看-多人伦精品一区二区三区视频-多色视频-免费黄色视屏网站-免费黄色在线

國內(nèi)最全IT社區(qū)平臺 聯(lián)系我們 | 收藏本站
阿里云優(yōu)惠2
您當(dāng)前位置:首頁 > php開源 > 綜合技術(shù) > 閉包漫談(從抽象代數(shù)及函數(shù)式編程角度)

閉包漫談(從抽象代數(shù)及函數(shù)式編程角度)

來源:程序員人生   發(fā)布時間:2014-09-23 15:50:20 閱讀次數(shù):3441次

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

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

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

  一個需要明確的重要事實

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

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

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

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

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

  集合的定義

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

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

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

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

  運算的定義

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

  例如,設(shè)N+是正整數(shù)集合,那么加法(+)和減法(−)都可以看做定義在N+上的二元運算,因為任意兩個正整數(shù)都可以進行加減。

  封閉的定義

  有了集合和運算的概念,就可以定義封閉的概念了。

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

  如果對于任意a1,a2∈A,有a1⊕a2∈A,那么說二元運算⊕對于集合A是封閉的。

  例如“+”對于N+是封閉的,因為任意兩個正整數(shù)的和結(jié)果仍然是正整數(shù);但是“−”對于N+不是封閉的,例如3和5屬于N+,但是:3−5=−2∉N+。

  閉包性質(zhì)

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

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

  一個例子 - 存在于形式語言與自動機理論中的閉包

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

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

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

  Σ∗=Σ0∪Σ1∪...∪Σn∪...

  此時集合Σ∗滿足閉包性質(zhì),因為這個集合對于冪運算是封閉的。有興趣的讀者可以自行證明一下。

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

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

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

  Lambda演算與自由變量

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

  簡單來說lambda演算將計算過程看過一系列的函數(shù)代換,例如,下面是加運算的lambda函數(shù)(假設(shè)+運算已經(jīng)定義):

λx.λy.x+y

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

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

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

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

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

(define (f x y) (+ x y))

  可以這樣計算7+2:

(f 7 2);Value: 9

  下面看一個稍微復(fù)雜點的例子:

(define (f x) (lambda (y) (+ x y)))

  這里定義了函數(shù)f,接受一個參數(shù)x,特別要注意它的返回值:不是一個值而是一個匿名函數(shù)。如果我們把這個函數(shù)單獨拿出來:

(lambda (y) (+ x y))

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

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

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

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

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

(define (f x) (lambda (y) (+ x y)))

  如果我們運行下面程序:

(f 7);Value 13: #[compound-procedure 13]

  可以看到,f返回了一個過程(匿名函數(shù)),按照函數(shù)演算規(guī)則,這個函數(shù)應(yīng)該是:

(lambda (y) (+ 7 y))

  那么下面的運算就很直觀了:

((f 7) 2);Value: 9

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

  上面的這段話不太好理解,但是請務(wù)必多讀幾遍,因為,這就是閉包的全部。

  從Scheme到JavaScript

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

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

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

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

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

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

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

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

function f(x){ return function(y) { return x + y; }; }

  可以執(zhí)行與上述scheme類似的計算(因為脫離了瀏覽器,我是用nodejs執(zhí)行這段JavaScript):

console.log( (f(7)) (2) );
//9

  其中f返回的匿名函數(shù)與其自由變量x組成了一個閉包系統(tǒng)。

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

function f(x{
    return function(y) {
        return x + y;
    };
}
var lam = f(7);
console.log(lam(2));

  總結(jié)

  本文分別討論了抽象代數(shù)和函數(shù)式編程中兩個截然不同的閉包概念,當(dāng)然,作為程序員我們更關(guān)心的是后者(但前者也不是對程序員一點用也沒有,如果學(xué)習(xí)函數(shù)式語言的構(gòu)造原理,抽象代數(shù)中的閉包也是必須理解的概念)。

  實際上,閉包遠沒有很多人想象的復(fù)雜和神秘,只不過是函數(shù)式編程中一個普通的概念,但是可能因為我們大多數(shù)人是從命令式編程語言學(xué)習(xí)編程,很少去寫函數(shù)式程序,而要理解閉包卻是需要結(jié)合函數(shù)式編程泛型,因此很難看清閉包的本質(zhì)。希望本文能對您有所幫助,同時我個人也建議可以學(xué)習(xí)一門函數(shù)式語言,這樣對很多概念的理解非常有好處。

生活不易,碼農(nóng)辛苦
如果您覺得本網(wǎng)站對您的學(xué)習(xí)有所幫助,可以手機掃描二維碼進行捐贈
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關(guān)閉
程序員人生
主站蜘蛛池模板: 欧美黑人xxxx性高清版 | 国产精品热久久毛片 | 中文字幕22页 | 国产 日韩欧美 | 欧美精品a毛片免费观看 | 日韩亚洲一区中文字幕 | 国产在线精品福利91香蕉 | 九九九精品午夜在线观看 | 高清不卡一区二区三区 | 欧美伊人久久大香线蕉在观 | 亚洲福利视频一区二区三区 | 中文字幕免费观看 | 亚洲国产一区二区在线 | 亚洲tv成人天堂在线播放 | 性做久久久久久久免费看 | 玖玖精品在线观看 | 国产欧美综合一区二区 | 影院亚洲 | 国产成人一区二区三区影院免费 | 亚洲区欧美 | 2020中文字幕| 亚洲精品久久久久午夜三 | 性色网站 | 伊人一本之道 | 免费观看黄色的网站 | 免费播放春色aⅴ视频 | 亚洲天堂精品在线 | 国产性色强伦免费看视频 | 成年人在线观看视频网站 | 午夜视频在线观看网站 | 国产精品一区不卡 | 免费xx| 久久免费观看国产精品 | 在线播放69热精品视频 | 欧美 日韩 国产在线 | 欧美一级视频免费观看 | 国产亚洲影院 | 日韩精品欧美国产精品亚 | 亚洲国产成人精品不卡青青草原 | 国产69精品久久久久99不卡 | 国产精品一区二区三区高清在线 |