emu in blogjava

            BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
            171 隨筆 :: 103 文章 :: 1052 評(píng)論 :: 2 Trackbacks

          繁體中文翻譯見: 你的程式語言可以這樣做嗎?

          emu翻譯的版本

          你的編程語言可以這樣做嗎?

          From The Joel on Software Translation Project

          你的編程語言可以這樣做嗎?

          有一天,你在瀏覽自己的代碼,發(fā)現(xiàn)有兩大段代碼幾乎一樣。實(shí)際上,它們確實(shí)是一樣的——除了一個(gè)關(guān)于意大利面(Spaghetti)而另一個(gè)關(guān)于巧克力慕思(Chocolate Moose)。

             // 一個(gè)小例子:
          alert("偶要吃意大利面!");
          alert("偶要吃巧克力慕思!");
          

          嗯,這個(gè)例子碰巧是用javascript寫的,不過你就算不懂JavaScript,應(yīng)該也能明白它在干什么。

          拷貝代碼不好。于是,你創(chuàng)建了個(gè)函數(shù)

             function SwedishChef( food )
          {
          alert("偶要吃" + food + "!");
          }
          
             SwedishChef("意大利面");
          SwedishChef("巧克力慕思");
          

           

          Ok,這只是一個(gè)很小很小的例子而已,相信你能想像到個(gè)更實(shí)際一點(diǎn)的例子。這段代碼有很多優(yōu)點(diǎn),你全都聽過幾萬次了:可維護(hù)性、可讀性、抽象性 = 好!

          現(xiàn)在你留意到有另外兩段代碼幾乎跟它們一模一樣,除了一個(gè)反復(fù)調(diào)用一個(gè)叫BoomBoom的函數(shù),另一個(gè)反復(fù)調(diào)用一個(gè)叫PutInPot的。除此之外,這兩段代碼簡(jiǎn)直沒什么兩樣:

          alert("拿龍蝦");
          PutInPot("龍蝦");
          PutInPot("水");
          alert("拿雞肉");
          BoomBoom("雞肉");
          BoomBoom("椰子醬");

          現(xiàn)在要想個(gè)辦法,使得你可以將一個(gè)函數(shù)用作另一個(gè)函數(shù)的參數(shù)。這是個(gè)重要的能力,因?yàn)槟愀菀讓⒖蚣艽a寫成一個(gè)函數(shù)(emu注:還記得template method模式吧?)

             function Cook( i1, i2, f )
          {
          alert("拿" + i1);
          f(i1);
          f(i2);
          }
          
             Cook( "龍蝦", "水", PutInPot );
          Cook( "雞肉", "椰子醬", BoomBoom );
          

          看看,我們居然把函數(shù)當(dāng)成調(diào)用參數(shù)傳遞了!

          你的編程語言能辦到嗎?

          等等……假如我們已經(jīng)有了PutInPot和BoomBoom這些函數(shù)的具體實(shí)現(xiàn)代碼(而且又不需要在別的地方重用它們),那么用內(nèi)聯(lián)語法把它們寫進(jìn)函數(shù)調(diào)用里面不是比顯式的聲明這兩個(gè)函數(shù)更漂亮嗎?

             Cook( "龍蝦",
          "水",
          function(x) { alert("pot " + x); }  );
          Cook( "雞肉",
          "椰子醬",
          function(x) { alert("boom " + x); } );
          

          耶,真方便!請(qǐng)注意我只是隨手創(chuàng)建了個(gè)函數(shù),甚至不用考慮怎么為它起名,只要拎著它的耳朵把它往一個(gè)函數(shù)里頭一丟就可以了。

          當(dāng)你一想到作為參數(shù)的匿名函數(shù),你也許想到對(duì)那些對(duì)數(shù)組里的每個(gè)元素進(jìn)行相同操作的代碼。

             var a = [1,2,3];
          
             for (i=0; i<a.length; i++){
          a[i] = a[i] * 2;
          }
          
             for (i=0; i<a.length; i++){
          alert(a[i]);
          }
          

          常常要對(duì)數(shù)組里的所有元素做同一件事,因此你可以寫個(gè)這樣的函數(shù)來幫忙:

             function map(fn, a){
          for (i = 0; i < a.length; i++){
          a[i] = fn(a[i]);
          }
          }
          

          現(xiàn)在你可以把上面的東西改成:

             map( function(x){return x*2;}, a );
          map( alert, a );
          

          另一個(gè)常見的任務(wù)是將數(shù)組內(nèi)的所有元素按照某總方式匯總起來:

             function sum(a){
          var s = 0;
          for (i = 0; i < a.length; i++)
          s += a[i];
          return s;
          }
          function join(a){
          var s = "";
          for (i = 0; i < a.length; i++)
          s += a[i];
          return s;
          }
          alert(sum([1,2,3]));
          alert(join(["a","b","c"]));
          

          sumjoin長(zhǎng)得很像,你也許想把它們抽象為一個(gè)將數(shù)組內(nèi)的所有元素按某種算法匯總起來的泛型函數(shù):

             function reduce(fn, a, init){
          var s = init;
          for (i = 0; i < a.length; i++)
          s = fn( s, a[i] );
          return s;
          }
          function sum(a){
          return reduce( function(a, b){ return a + b; }, a, 0 );
          }
          function join(a){
          return reduce( function(a, b){ return a + b; }, a, "" );
          }
          

          許多早期的編程語言沒法子做這種事。有些語言容許你做,卻又困難重重(例如C有函數(shù)指針,但你要在別處聲明和定義函數(shù))。面向?qū)ο笳Z言也不確保你用函數(shù)可以干些啥(把函數(shù)當(dāng)對(duì)象處理?)。

          如果你想將函數(shù)視為一類對(duì)象,Java要求你建立一個(gè)有單方法的對(duì)象,稱為算子對(duì)象。許多面向?qū)ο笳Z言要你為每個(gè)類都建立一個(gè)完整文件,像這樣開發(fā)可真叫快。如果你的編程語言要你使用算子對(duì)象來包裝方法(而不是把方法本身當(dāng)成對(duì)象),你就不能徹底得到現(xiàn)代(動(dòng)態(tài))編程語言的好處。不妨試試看你可否退貨拿回些錢?

          不用再寫那些除了經(jīng)過一個(gè)數(shù)組對(duì)每個(gè)元素做一些事情之外一無是處的函數(shù),有什么好處?

          讓我們看回map函數(shù)。當(dāng)你要對(duì)數(shù)組內(nèi)的每個(gè)元素做一些事,你很可能不在乎哪個(gè)元素先做。無論由第一個(gè)元素開始執(zhí)行,還是是由最后一個(gè)元素執(zhí)行,你的結(jié)果都是一樣的,對(duì)不?如果你手頭上有2個(gè)CPU,你可以寫段代碼,使得它們各對(duì)一半的元素工作,于是乎map快了兩倍。

          或者,發(fā)揮一下想像力,設(shè)想你在全球有千千萬萬臺(tái)服務(wù)器分布在全世界的若干個(gè)數(shù)據(jù)中心,你有一個(gè)真的很大很大的數(shù)組,嗯,再發(fā)揮一下想像力,設(shè)想這個(gè)數(shù)組記錄有整個(gè)互聯(lián)網(wǎng)的內(nèi)容。好了,現(xiàn)在你可以在幾千臺(tái)服務(wù)器上同時(shí)執(zhí)行map,讓每臺(tái)服務(wù)器都來解決同一個(gè)問題的一小部分。

          那么在這個(gè)例子里面,編寫一段非常快的代碼來搜索整個(gè)互聯(lián)網(wǎng)這個(gè)問題,其實(shí)就和用一個(gè)簡(jiǎn)單的字符串搜索器(算子)作為參數(shù)來調(diào)用map函數(shù)一樣簡(jiǎn)單了。

          希望你注意到一個(gè)真正有意思的要點(diǎn),如果你想要把map/reduce模式變成一個(gè)對(duì)所有人都有用,對(duì)所有人都能立刻派上用場(chǎng)的技術(shù),你只需要一個(gè)超級(jí)天才來寫最重要的一部分代碼,來讓map/reduce可以在一個(gè)巨大的并行計(jì)算機(jī)陣列上運(yùn)行,然后其他舊的但是一向在單一個(gè)循環(huán)中運(yùn)行良好的代碼,仍可以保持正確的運(yùn)行,惟一的差別只是比原來單機(jī)運(yùn)行快了n倍。這意味著它們都一不留神突然變成可以被用來解決一個(gè)巨大的問題的代碼。

          讓我再啰嗦一下,通過把“循環(huán)”這個(gè)概念加以抽象,你可以把用任何你喜歡的方式來實(shí)現(xiàn)“循環(huán)”過程,包括可以實(shí)現(xiàn)讓循環(huán)迭代速度隨著硬件計(jì)算能力保持令人滿意的同步增長(zhǎng)。

          你現(xiàn)在應(yīng)該可以明白不久為何對(duì)那些對(duì)除了Java之外什么都沒被學(xué)過的計(jì)算機(jī)系學(xué)生表示不滿了:  (http://www.joelonsoftware.com/articles/ThePerilsofJavaSchools.html)

          不理解函數(shù)式編程,你就發(fā)明不了像MapReduce這樣讓Google的計(jì)算能力如此具有可擴(kuò)展性的算法。Map和Reduce這兩個(gè)術(shù)語源自Lisp語言和函數(shù)式編程.MapReduce概念對(duì)于任何還能記得他們的6.001-equivalent編程課上講過“真正的函數(shù)式的程序應(yīng)該沒有任何副作用,可以輕易并行運(yùn)行”的人來說是非常容易理解的。Google發(fā)明了MapReduce而微軟沒有,這一定程度上可以解釋了為什么在google已經(jīng)轉(zhuǎn)下了他們的下一個(gè)目標(biāo)(建設(shè)世界上最大型的超級(jí)并行計(jì)算機(jī)陣列Skynet)的時(shí)候微軟還在想方設(shè)法讓他們的最基礎(chǔ)的搜索程序跑起來。我不覺得微軟能完全了解在這一波浪潮中他們落后了多遠(yuǎn)。

          我希望你現(xiàn)在明白,把函數(shù)當(dāng)成基本類型的(動(dòng)態(tài))編程語言能讓你在編程過程中更好的進(jìn)行抽象化,也就是使代碼精悍、功能更內(nèi)聚、更具可重用性及更具有擴(kuò)展性。很多的Google應(yīng)用使用Map/Reduce模式,因此一有人對(duì)其優(yōu)化或修正缺陷,它們就都可以從中得益。

          我準(zhǔn)備要再羅嗦一下,我認(rèn)為最有生產(chǎn)力的編程語言莫過于能讓你在不同層次上都可以進(jìn)行抽象化的。老掉牙的FORTRAN 語言以前是不讓你寫函數(shù)的。C 有函數(shù)指針,可是它們都非常丑丑丑丑丑丑丑丑陋,不允許匿名聲明,又不能在用它們時(shí)實(shí)現(xiàn)它們而偏偏要放在別處去實(shí)現(xiàn)。Java讓你使用算子對(duì)象,一種更丑陋的東西。正如Steve Yegge所述,Java是個(gè)名詞王國(guó) (http://steve-yegge.blogspot.com/2006/03/execution-in-kingdom-of-nouns.html)

          作者注:這里提起了FORTRAN,不過我上次使用FORTRAN是27年前的事了。FORTRAN是有函數(shù)的,我碼字那會(huì)兒腦子里面想的大概是GW-BASIC語言。(emu注,basic確實(shí)只有所謂的子程序和go-sub語句,作用只是重新組織代碼結(jié)構(gòu)而已,沒有參數(shù)和調(diào)用堆棧,因此沒有真正的函數(shù)調(diào)用)

           
          posted on 2006-08-08 11:10 emu 閱讀(8678) 評(píng)論(8)  編輯  收藏

          評(píng)論

          # re: [轉(zhuǎn)]你的編程語言可以這樣做嗎?(map/reduce的js示范,emu略加整理) 2006-08-08 13:41 風(fēng)之石
          呵呵,就翻譯過來啦,受益了! ^_^  回復(fù)  更多評(píng)論
            

          # re: [轉(zhuǎn)]你的編程語言可以這樣做嗎?(map/reduce的js示范,emu整理) 2006-08-10 15:20 randomVisitor
          Haskell can do those easily.
            回復(fù)  更多評(píng)論
            

          # re: [轉(zhuǎn)]你的編程語言可以這樣做嗎?(map/reduce的js示范,emu整理) 2006-08-10 21:31 emu
          嗯,不只haskell,很多動(dòng)態(tài)語言都支持這樣做。原作者起了《你的編程語言可以這樣做嗎》這個(gè)標(biāo)題其實(shí)并不是這篇文章的真正價(jià)值所在,我譯這篇文章也不是因?yàn)樵髡呖梢园颜Z言的初級(jí)技巧玩得轉(zhuǎn),而是因?yàn)檫@是一篇map/reduce模型的示范。  回復(fù)  更多評(píng)論
            

          # re: 你的編程語言可以這樣做嗎?(map/reduce的js示范,emu翻譯整理) 2006-08-30 17:43 王耀
          不錯(cuò),做我?guī)煾蛋?QQ5156635  回復(fù)  更多評(píng)論
            

          # re: 你的編程語言可以這樣做嗎?(map/reduce的js示范,emu翻譯整理) 2006-09-09 10:49 創(chuàng)億無限
          不喜歡java  回復(fù)  更多評(píng)論
            

          # re: 你的編程語言可以這樣做嗎?(map/reduce的js示范,emu翻譯整理) 2006-09-11 17:23 emu
          樓上是過來打自己網(wǎng)站的廣告的?  回復(fù)  更多評(píng)論
            

          # re: 你的編程語言可以這樣做嗎?(map/reduce的js示范,emu翻譯整理) 2006-09-21 09:34 sf
          不錯(cuò)  回復(fù)  更多評(píng)論
            

          # re: 你的編程語言可以這樣做嗎?(map/reduce的js示范,emu翻譯整理) 2009-02-24 09:41 rocman
          娃哈哈,ruby 也支持這樣的~  回復(fù)  更多評(píng)論
            


          只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 闻喜县| 中方县| 溧阳市| 青铜峡市| 秦安县| 边坝县| 普兰县| 兴隆县| 郁南县| 贵溪市| 鄂托克前旗| 永和县| 万安县| 阿合奇县| 水城县| 安平县| 灵宝市| 凌云县| 游戏| 宁城县| 林口县| 常山县| 廉江市| 南川市| 类乌齐县| 洪湖市| 秭归县| 富顺县| 菏泽市| 南乐县| 建平县| 阿克| 定西市| 凤山县| 阿拉善右旗| 泰宁县| 浪卡子县| 苏州市| 青岛市| 祁连县| 紫云|