【永恒的瞬間】
          ?Give me hapy ?
          				for
          				(
          				
          						Iterator
          				 i = c.iterator(); i.hasNext(); ){String s = (String) i.next();
              ...
          }

          現(xiàn)在,多虧了這種新的優(yōu)雅的迭代語(yǔ)法,以及泛型的引入,我們可以用下面的代碼:

          				for
          				(
          				
          						String
          				 s : c){
              ...
          }

          很明顯,可以少輸入很多字符。但請(qǐng)考慮一個(gè)問(wèn)題:為什么花了10年才引入了這個(gè)特性?不過(guò)我們先不考慮這個(gè)重要的問(wèn)題,再看看另一種語(yǔ)言,Python。

          Python在很多方面都是一個(gè)更加適合編程的語(yǔ)言。它的衍化要比Java快得多。例如,Python 2.2引入了生成器(generator)。一個(gè)生成器是一個(gè)可以產(chǎn)生多個(gè)值得函數(shù),在每次調(diào)用時(shí)都會(huì)保存狀態(tài)。下面是一個(gè)簡(jiǎn)單的例子:

          				def counter(n):
            whileTrue:
              yield n
              n = n + 1

          因?yàn)檫@個(gè)函數(shù)定義包含了關(guān)鍵詞yield,所以Python就可以知道它是一個(gè)生成器。可以像下面這樣使用counter生成器:

          c12 = counter(12)
          c12.next()
          c12.next()

          第一行創(chuàng)建了從12開始計(jì)數(shù)的生成器的實(shí)例。第二行告訴生成器運(yùn)行到產(chǎn)生(yield)一個(gè)值為止。第三行告訴生成器繼續(xù)運(yùn)行直到產(chǎn)生了另一個(gè)值。該生成器所產(chǎn)生的前兩個(gè)值分別是整數(shù)12和13。

          這確實(shí)是一個(gè)很酷的特性:它讓程序員能寫出更簡(jiǎn)單的代碼,而不會(huì)使生成器變得復(fù)雜和容易出錯(cuò)。為何Java不能學(xué)習(xí)Python呢?

          我們也先將第二個(gè)問(wèn)題放一邊,思考一下如何用另一種語(yǔ)言來(lái)實(shí)現(xiàn)Python的生成器,這種語(yǔ)言就是Scheme——世界上有一些自以為是的怪人就用它。Scheme是Lisp的一種方言,它從誕生到現(xiàn)在已經(jīng)存在了大約30年了。Lisp則已經(jīng)存在了大約50年了。

          下面是我可以完成的對(duì)Python中counter生成器模仿最好的Scheme實(shí)現(xiàn):


          對(duì)資深Lisp程序員多說(shuō)一句:下面我要演示的子程序要比官方的累加器例子復(fù)雜得多,這是因?yàn)槲沂前凑樟?a >Python生成器的語(yǔ)義來(lái)寫的。


          				(
          				define
          				(counter n)(letrec((generator
                      (lambda(yield)(let counter ((n n))(call-with-current-continuation(lambda(continue)(set! generator (lambda(k)(set! yield k)(continue n)))(yield n)))(counter (+ n 1))))))(lambda()(call-with-current-continuation(lambda(yield)(generator yield))))))

          “我靠!”你可能會(huì)有這種反應(yīng)。確實(shí)太復(fù)雜了!在寫這段代碼的最初版本的時(shí)候,我說(shuō)寫這個(gè)不會(huì)太難。然后我發(fā)現(xiàn)了一個(gè)可能導(dǎo)致死循環(huán)的錯(cuò)誤,而引發(fā)錯(cuò)誤不是小概率事件。所以最后我認(rèn)同了這點(diǎn):如果只是要寫一個(gè)能和Python生成器效果一樣的函數(shù),還是不要寫這樣的子過(guò)程的比較好。不過(guò),這個(gè)可怕的東西在客戶端代碼使用起來(lái)卻十分簡(jiǎn)單:

          				(
          				define c12 (counter 12))(c12)(c12)

          第一行定義了c12是子過(guò)程counter給一個(gè)參數(shù)12調(diào)用時(shí)的結(jié)果。第二行和第三行直接調(diào)用c12,沒(méi)有任何參數(shù),就和Python的例子一樣,返回了12和13。不過(guò)這些都是學(xué)院派的,沒(méi)有哪個(gè)瘋子會(huì)在普通需求下寫一個(gè)這樣的子過(guò)程。

          寫像counter這樣的子過(guò)程一般會(huì)導(dǎo)致手指抽痙、頭腦發(fā)脹。不過(guò),有意思的是,我們可以跳過(guò)這些來(lái)寫counter,Scheme的生成器要比Python版本的更加容易使用,因?yàn)镾cheme的返回的是函數(shù),而Python生成器返回的是生成器對(duì)象,所以Python生成器需要調(diào)用next方法。

          (旁白:Python生成器的設(shè)計(jì)師們本可以這樣實(shí)現(xiàn)生成器對(duì)象:接下來(lái)的值通過(guò)c12()c12.next()來(lái)獲取,不過(guò)他們并沒(méi)有這樣實(shí)現(xiàn)。)

          回到Scheme上……在Scheme中寫這樣的生成器的復(fù)雜和容易出錯(cuò)看上去似乎讓在Scheme中使用生成器變得不切實(shí)際,但實(shí)際上并非如此,因?yàn)镾cheme包含了一個(gè)Python和Java都缺乏的特性:擴(kuò)展語(yǔ)言語(yǔ)法的能力。如果你能夠?qū)懗鯯cheme版本的counter,花不了多少功夫就可以創(chuàng)建一個(gè)宏(macro)使得這個(gè)特性能以一種可以被大家接受的方式使用。下面是我寫的宏,可以完成這個(gè)任務(wù):

          				(
          				define-syntax define-generator
            (syntax-rules()((define-generator (NAME ARG ...) YIELD-PROC E1 E2 ...)(define(NAME ARG ...)(letrec((generator
                           (lambda(yield)(let((YIELD-PROC
                                    (lambda v
                                      (call-with-current-continuation(lambda(continue)(set! generator (lambda(k)(set! yield k)(apply continue v)))(apply yield v))))))(let NAME ((ARG ARG) ...)
                                 E1 E2 ...)))))(lambda()(call-with-current-continuation(lambda(yield)(generator yield)))))))))

          一旦有了這個(gè)宏,counter生成器的Scheme版本就可以這樣定義了:

          				(define-generator (counter n) yield
            (counter (+ 1(yield n))))

          還不錯(cuò)吧?這個(gè)版本唯一讓我煩的地方是必須指定yield函數(shù)的名稱。不過(guò)它還是給予程序員一些靈活性,可以根據(jù)代碼的上下文來(lái)給函數(shù)起一個(gè)最有意義的名稱。(其實(shí),資深的Lisp程序員應(yīng)該知道這個(gè)“特性”可以使用一些非hygenic宏來(lái)修正,不過(guò)這里我們還是堅(jiān)持標(biāo)準(zhǔn)R5RS Scheme)。

          如果你比較一下第一版和第二版的counter,你可能會(huì)注意到我在新的define-generator版本中作了一些小手腳:yield函數(shù)返回了它產(chǎn)生的值,因此它可以用于對(duì)counter的遞歸調(diào)用中。而Python的生成器就不能這樣用。

          那么為什么Java不能變得更像Python?答案是——其實(shí)Java和Python很像:Python的用戶也等了將近10年才可以用上生成器。而我在玩了幾天之后花了幾個(gè)小時(shí)就在Scheme中加入了對(duì)生成器的支持。不過(guò)還是有人會(huì)說(shuō)生成器以及最近的其他一些Python特性,如列表包容,都使得Python變得更加容易編寫——我當(dāng)然完全同意這個(gè)觀點(diǎn)——但是,從根本上來(lái)說(shuō),Java和Python在這一點(diǎn)上是一樣的——都不能修改語(yǔ)言本身。

          Java、Python和幾乎所有其他非Lisp語(yǔ)言都是讓你任由語(yǔ)言設(shè)計(jì)者擺布。你要等他們實(shí)現(xiàn)你需要的語(yǔ)言特性,可能只有列表中的前幾個(gè)。而且等他們弄個(gè)一個(gè)新東西給你搔搔癢,誰(shuí)能確定你喜歡這種結(jié)果?

          那么為何花了10年才在Java中有了增強(qiáng)迭代語(yǔ)法?這是因?yàn)樵贘ava和很多其他編程語(yǔ)言一樣,語(yǔ)法是一個(gè)大問(wèn)題。一般用戶都不會(huì)修改語(yǔ)言的語(yǔ)言。因?yàn)檫@很難完成,只有少數(shù)人學(xué)習(xí)了這種技術(shù)才能去做。當(dāng)需要修改語(yǔ)法時(shí),表達(dá)力和清晰的優(yōu)先級(jí)都沒(méi)有保持向后兼容重要。

          在Scheme中,加入語(yǔ)法相對(duì)還比較簡(jiǎn)單,同時(shí)還可以根據(jù)特定問(wèn)題的基礎(chǔ)來(lái)完成,所以無(wú)須擔(dān)心是有要給出一個(gè)普遍適用的理想解決方案。這種能根據(jù)問(wèn)題構(gòu)建語(yǔ)言的能力要?jiǎng)龠^(guò)對(duì)使用很多括號(hào)的語(yǔ)言的擔(dān)心。

          posted on 2007-01-12 20:28 ???MengChuChen 閱讀(168) 評(píng)論(0)  編輯  收藏

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


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 璧山县| 榆社县| 都匀市| 永春县| 阜新市| 开阳县| 微山县| 贡嘎县| 朝阳县| 盈江县| 涿鹿县| 洪洞县| 新郑市| 宜兰市| 扎赉特旗| 同仁县| 安康市| 伊宁县| 卓尼县| 花垣县| 长汀县| 突泉县| 元阳县| 利川市| 鹿邑县| 阳高县| 合山市| 柳河县| 搜索| 剑阁县| 杭州市| 娱乐| 衡阳县| 繁昌县| 长武县| 卢龙县| 大邑县| 太保市| 洛阳市| 汶川县| 尼木县|