我們在開發中經常聽到閉包這個概念,為什麼要有閉包的概念,我總是感覺閉包這個概念很雞肋,那麼最初設計閉包的人是怎麼想的。
為什麼要有閉包?
閉包的定義
首先閉包有概念之後才能問上面的問題,先給閉包一個概念。
在電腦科學中,閉包(英語:Closure),又稱詞法閉包(Lexical Closure)或函式閉包(function closures),是引用了自由變數的函式。
上面是維基百科的定義,定義比較簡單,首先閉包是個函式,其次就是引用了一個字變數,好像問題沒有這樣簡單。
class A {
int sum = 0;
int sum(int a) {
return a = a + sum;
}
}
複製程式碼
按照上面的說明,java中sum這個函式就是閉包的。是維基百科的概念是否有問題,還是我的java世界崩塌了。
另一種說法認為閉包是由函式和與其相關的引用環境組合而成的實體。
這個定義是閉包的概念、形式與應用文章中的定義,看上去沒有什麼大的區別,但是這裡強調的是一個引用環境的整體。看上去還是很難理解,我們先放下概念,回頭看能不能自己給出屬於自己的定義。
閉包的由來
計算機的目的就是計算,也可以這樣狹隘的理解,計算機要能完成可計算公式的計算和邏輯。要完成這樣的事情,需要一套東西去支援。
阿隆佐邱奇(Alonzo Church)發明了Lambda演算,也就是λ演算。
Lambda演算
λ演算(英語:lambda calculus,λ-calculus)是一套從數學邏輯中發展,以變數繫結和替換的規則,來研究函式如何抽象化定義、函式如何被應用以及遞迴的形式系統。
因為這裡Lambda演算不是討論的重點,我舉幾個簡單的例子,讓大家明白就好。
在Lambda演算中,每個表示式都代表一個函式,這個函式有一個引數,並且會返回一個值。也就是說在Lambda演算中只有函式。
-
Lambda演算的基本定義:
λx. E,x是引數,並且有且僅有一個引數,E是函式體。
-
函式的應用:
E1 E2,E1是個函式,E2也是個函式,並且每個函式都有返回值,E2的返回值當成λx. E的x,帶人到E1的函式中,在返回E1的結果。
這樣還是很難理解,再來個個例子。
現在我們定義一個數學函式f(x) = x + 2,數學意義很明顯,就給x加上2。怎麼用Lambda演算弄?
-
Lambda演算的基本定義:
λx. x + 2,x是引數,x + 2函式體。
-
函式的應用:
當有個引數λa.a 3的時候就是這個樣子(λx. x + 2) (λa.a 3),可以寫成也就符合上面的E1 E2格式。結果就是(λx. x + 2)3 = 3 + 2 = 5
這裡有沒有特別的熟悉,說說程式設計函式定義。
returnValue funcitonName(parameter){
methodBody
}
複製程式碼
假如這個函式只能傳入一個引數,那麼是不是就是 λx. E的簡單表達,原來函式的由來可以這樣追溯。
Lambda演算的加法問題
Lambda演算只支援一個引數,我想計算f(x, y) = x + y怎麼算呢,λx y. x + y,這樣是違反規則的,不要著急我們可以採用Currying的方式,λx.λy.x + y,呼叫的時候是這樣的(λx.λy.x + y) (7 2) = (λy.7 + y)(2) = (7 + 2) = 9。想一下程式怎樣寫。
function add(x){
return (function(y) {
return x+y;
});
}
add(100)(12);
複製程式碼
這個不是閉包嗎,我們繞了一大圈終於把閉包的源頭找到了,閉包是這樣來的。
現在我們嘗試一下給閉包下個定義,首先閉包應該是函式內套用函式,這樣我的java世界會來了,因為java是類裡面有函式,函式是不能套用函式的(語法是這樣的)。
這是嘗試下個定義,函式本身被當做引數傳入函式中,並且函式直接有特殊的作用域。
相關文章
-
我的最愛Lambda演算,一篇關於Lambda演算的文章。
-
閉包的概念、形式與應用,來自IBM的文章,很不錯。
-
符號: 抽象、語義,從符號的角度介紹了Lambda演算。