如果乙個程式語言能夠用高階函式解決問題,則意味著資料作用域問題已十分突出。當函式可以當成引數和返回值在函式之間進行傳遞時,編譯器利用閉包擴充套件變數的作用域,以保證隨時能得到所需要的資料。
c#函式式程式設計之作用域
在c#中,變數的作用域是嚴格確定的。其本質是所有**生存在類的方法中、所有變數只生存於宣告它們的模組中或者之後的**中。變數的值是可變的,乙個變數越是公開,帶來的問題就越嚴重。一般的原則是,變數的值最好保持不變,或者在最小的作用域內儲存其值。乙個純函式最好只使用在自己的模組中定義的變數值,不訪問其作用域之外的任何變數。
遺憾的是,有時我們無法把變數的值限制於函式的範圍內。如果在程式的初始化時定義了幾個變數,在後面需要反覆用到它們,怎麼辦?乙個可能的辦法是使用閉包。
c#函式式程式設計之閉包機制
為了理解閉包的本質,我們分析幾個使用閉包的例子:
namespace closuresstatic funcgetclosurefunc()
}}
此**的結果輸出是多少?答案是20 40 60,前面兩個值,大家應該很容易就能看出來,但第三個值為什麼是60呢?先來看看程式的執行流程:closures函式呼叫getclosurefunc函式並進入其中。函式呼叫語句中帶了乙個引數30。這是由於getclosurefunc返回的是乙個函式,即執行時再次呼叫了這個函式,進入getclosurefunc函式中,首先val的值為10,通過internaladd方法傳入乙個值10,因此第乙個輸出值為20,往下走,val的值變成30,通過internaladd方法傳入值10,於是第二個輸出值為40。從這裡我們大致可以看出,區域性函式和區域性變數如何在同乙個作用域中起作用,顯然,對區域性變數的改變會影響internaladd的值,儘管變數的改變發生在internaladd最初的建立之後。最後,getclosurefunc返回了internaladd方法,以引數30再次呼叫這個函式,於是,結果成為60。
初看起來,這並不真正符合邏輯。val應該是乙個區域性變數,它生存在棧中,當getclosurefunc函式返回時,它就不在了,不是麼?確實如此,這正是閉包的目的,當編譯器會明白無誤地警告這種情況會引起程式的崩潰時阻止變數值超出其作用域之外。
從技術角度來看,資料儲存的位置很重要,編譯器建立乙個匿名類,並在getclosurefunc中建立這個類的例項——如果不需要閉包起作用,則那個匿名函式只會與getclosurefunc生存在同乙個類中,最後,區域性變數val實際上不再是乙個區域性變數,而是匿名類中的乙個字段。其結果是,internaladd現在可以引用儲存在匿名類例項中的函式。這個例項中也包含變數val的資料。只要保持internaladd的引用,變數val的值就一直儲存著。
下面這段**說明編譯器在這種情形下採用的模式:
private sealed class displayclassprivate static funcgetclosurefunc()
}
回到動態建立函式思想:現在可以憑空建立新的函式,而且它的功能因引數而異。例如,下面這個函式把乙個靜態值加到乙個引數上:
private static void dynamicadd()private static funcgetaddx(int staticval)
這個原理正是許多函式構建技術的基礎,這種方法顯然與方法過載等物件導向方法相對應。但是與方法過載不同,匿名函式的建立可以在執行時動態發生,只需受另乙個函式中的一行**觸發。為使某個演算法更加容易讀和寫而使用的特殊函式可以在呼叫它的方法中建立,而不是再類級別上胡亂新增函式或方法——這正是函式模組化的核心思想。
總結
閉包是程式語言支援函式式設計方法的乙個重要工具。
C 函式式程式設計之用閉包封裝資料
如果乙個程式語言能夠用高階函式解決問題,則意味著資料作用域問題已十分突出。當函式可以當成引數和返回值在函式之間進行傳遞時,編譯器利用閉包擴充套件變數的作用域,以保證隨時能得到所需要的資料。c 函式式程式設計之作用域 在c 中,變數的作用域是嚴格確定的。其本質是所有 生存在類的方法中 所有變數只生存於...
Python函式式程式設計之閉包
def div fun n def div check fun x return x n 0 return div check fun這是什麼?外層函式中巢狀了乙個函式,然後外層函式將內層函式作為返回值進行返回,同時,返回函式中的計算也擁有了外層函式的變數n,這種將外部函式的引數和自身的區域性變數儲...
函式式程式設計 閉包
def curve pie a 25 defcurve x return a pow x,2 return curve f curve pie print f 2 輸出結果 ans 100 檢驗函式是否閉包 print f.closure 環境變數 a 25 print f.closure 0 ce...