ivaneeo's blog

          自由的力量,自由的生活。

            BlogJava :: 首頁 :: 聯(lián)系 :: 聚合  :: 管理
            669 Posts :: 0 Stories :: 64 Comments :: 0 Trackbacks
           隨著人們對動態(tài)語言興趣的日益濃厚,越來越多的人都遇到了閉包(Closures )和或塊(Blocks)等概念。有著C/C++/Java/C#等語言背景的人因為這些語言本身沒有閉包這個概念,所以可能不太了解閉包。本文將簡單的介紹一下閉包的概念,那些有大量支持閉包語言編程經(jīng)驗的人也許覺得本文不會太有意思。

              閉包的概念已經(jīng)提出很長時間了。我第一次碰到這它是在smalltalk中,那時候還叫做塊(blocks)。Lisp語言中用的很多。Ruby中也有同樣的功能-這也是Ruby用戶喜歡Ruby的一個原因。

              本質(zhì)上來說,一個閉包是一塊代碼,它們能作為參數(shù)傳遞給一個方法調(diào)用。我將通過一個簡單的例子來闡述這個觀點。假設(shè)我們有一個包含一些雇員對象的列表,然后我想列出職位為經(jīng)理的員工,這樣的員工可以通過IsManager判斷。在C#里,我們可能會寫出下面類似的代碼:

            public static IList Managers(IList emps) {
              IList result = new ArrayList();
              foreach(Employee e in emps)
                if (e.IsManager) result.Add(e);
              return result;
            }
          

              在一種支持閉包的語言中,比如Ruby,我們可以這樣寫:

            def managers(emps)
          	return emps.select {|e| e.isManager}
            end
            

              select是Ruby中定義的集合結(jié)構(gòu)中的一個方法,它接受一個block,也就是閉包,作為一個參數(shù)。在Ruby中,閉包寫在一對大括號中(不止這一種方法,另一種為do .. end)。如果這個塊也接受參數(shù),你可以將這些參數(shù)放到兩個豎線之間。select方法循環(huán)迭代給定的數(shù)組,對每個元素執(zhí)行給定的block,然后將每次執(zhí)行block返回true的元素組成一個新的數(shù)組再返回。

              現(xiàn)在,如果你是C程序員你也許要想,通過函數(shù)指針也可以實現(xiàn),如果你是JAVA程序員,你可能回想我可以用匿名內(nèi)類來實現(xiàn),而一個C#者則會想到代理(delegate)。這些機制和閉包類似,但是它們和閉包之間有兩個明顯得區(qū)別。

              第一個是形式上的不同(The first one is a formal difference)。閉包可以引用它定義時候可見的變量。看看下面的方法:

          def highPaid(emps)
          	threshold = 150
          	return emps.select {|e| e.salary > threshold}
          end
            

              注意select的block代碼中引用了在包含它的方法中的局部變量,而其它不支持真正閉包的語言使用其它方法達到類似功能的方法則不能這樣做。閉包還允許你做更有趣的事情,比如下面方法:

          def paidMore(amount)
          	return Proc.new {|e| e.salary > amount}
          end
          

              這個方法返回一個閉包,實際上它返回一個依賴于傳給它的參數(shù)的閉包。我可以用一個參數(shù)創(chuàng)建一個這樣的方法,然后再把它賦給另一個變量。

          highPaid = paidMore(150)
          

              變量 highPaid 包含了一段代碼(在Ruby中是一個Proc對象),這段代碼將判斷一個對象的salary屬性是否大于150。我們可以這樣使用這個方法:

          john = Employee.new
          john.salary = 200
          print highPaid.call(john)
            

                表達式highPaid.call(john)調(diào)用我之前定義的代碼,這時候此代碼中的amount已經(jīng)在創(chuàng)建這方法的時候綁定為150。即使現(xiàn)在我執(zhí)行print 的時候,150已經(jīng)不在它的范圍內(nèi)了,但是amount和150之間的綁定依然存在。

              所以,閉包的第一個關(guān)鍵點是閉包是一段代碼加上和定義它的環(huán)境之間的綁定(they are a block of code plus the bindings to the environment they came from)。這是閉包和函數(shù)指針等其它相似技術(shù)的不同點(java匿名內(nèi)類可以訪問局部變量,但是只有當這些內(nèi)類是final的時候才行)。

              第二個不同點不是定義形式的不同,但是也同樣重要。(The second difference is less of a defined formal difference, but is just as important, if not more so in practice)。支持閉包的語言允許你用很少的語法去定義一個閉包,盡管這點可能不是很重要的一點,但我相信這點是至關(guān)重要的-這是使得人們能很自然的使用閉包的關(guān)鍵點。看看Lisp,Smalltalk和Ruby,閉包遍布各處-比其它語言中類似的使用多很多。綁定局部變量是它的特點之一,但我想最大的原因是使用閉包的語法和符號非常簡單和清楚。

              一個很好的相關(guān)例子是從Smalltalk程序員到JAVA程序員,開始時很多人,包括我,試驗性的將在Smalltalk中使用閉包的地方在Java中使用匿名內(nèi)類來實現(xiàn)。但結(jié)果使得代碼變得混亂難看,所以我們不得不放棄。

             我在Ruby經(jīng)常使用閉包,但我不打算創(chuàng)建Proc對象,然后傳來傳去。大多數(shù)時間我用閉包來處理前面我提到的select等基于集合對象的方法。閉包另一個重要用途是'execute around method',比如處理一個文件:

          File.open(filename) {|f| doSomethingWithFile(f)}
          

             這里open方法打開一個文件,然后執(zhí)行給定的block,然后關(guān)閉它。這樣處理非常方便,尤其是對事務(wù)(要求commit或者rollback),或者其它的你需要在處理結(jié)束時候作一些收尾處理的事情。我在我的xml文檔轉(zhuǎn)換中廣泛使用這個優(yōu)點。

             閉包的這些用法顯然遠不如用Lisp語言的人遇到的多,即使我,在使用沒有閉包支持的語言的時候,也會想念這些東西。閉包就像一些你第一眼見到覺得不怎么樣的東西,但你很快就會喜歡上它們。

          posted on 2005-08-23 17:06 ivaneeo 閱讀(249) 評論(0)  編輯  收藏 所屬分類: ruby-寶石也鋒芒
          主站蜘蛛池模板: 三台县| 明溪县| 洪洞县| 大化| 古蔺县| 广饶县| 惠安县| 玛曲县| 大悟县| 嘉义县| 武平县| 淳化县| 滕州市| 梧州市| 东安县| 咸阳市| 汉源县| 澄江县| 盐边县| 五寨县| 嘉荫县| 泰州市| 溆浦县| 萝北县| 涞水县| 凯里市| 仙游县| 阜宁县| 安西县| 阿尔山市| 耿马| 麻城市| 丽水市| 施甸县| 嘉祥县| 个旧市| 沭阳县| 泰和县| 斗六市| 黑山县| 重庆市|