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

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

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

          很明顯,可以少輸入很多字符。但請考慮一個問題:為什么花了10年才引入了這個特性?不過我們先不考慮這個重要的問題,再看看另一種語言,Python。

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

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

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

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

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

          這確實是一個很酷的特性:它讓程序員能寫出更簡單的代碼,而不會使生成器變得復雜和容易出錯。為何Java不能學習Python呢?

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

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


          對資深Lisp程序員多說一句:下面我要演示的子程序要比官方的累加器例子復雜得多,這是因為我是按照了Python生成器的語義來寫的。


          				(
          				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))))))

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

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

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

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

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

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

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

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

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

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

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

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

          那么為何花了10年才在Java中有了增強迭代語法?這是因為在Java和很多其他編程語言一樣,語法是一個大問題。一般用戶都不會修改語言的語言。因為這很難完成,只有少數(shù)人學習了這種技術(shù)才能去做。當需要修改語法時,表達力和清晰的優(yōu)先級都沒有保持向后兼容重要。

          在Scheme中,加入語法相對還比較簡單,同時還可以根據(jù)特定問題的基礎(chǔ)來完成,所以無須擔心是有要給出一個普遍適用的理想解決方案。這種能根據(jù)問題構(gòu)建語言的能力要勝過對使用很多括號的語言的擔心。

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

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


          網(wǎng)站導航:
           
          主站蜘蛛池模板: 肥东县| 绥滨县| 宝应县| 博野县| 富蕴县| 兴宁市| 万山特区| 万年县| 北流市| 晋城| 龙游县| 聊城市| 邵阳县| 楚雄市| 郁南县| 西峡县| 铁岭县| 淄博市| 交口县| 星座| 巴塘县| 明溪县| 舒兰市| 桑日县| 赣榆县| 类乌齐县| 舒城县| 嘉峪关市| 淮北市| 西林县| 礼泉县| 开鲁县| 余姚市| 修武县| 富宁县| 林芝县| 威信县| 嘉黎县| 平武县| 望城县| 长沙县|