Cultured Perl: 吸引 C 和 Java 程序員目光的 Perl 5.6
Teodor Zlatanov 筆者側(cè)重于闡述 Perl 與 C 或 Java 不同的獨特之處,。您一定會為 Perl 這些在其他語言中看不到的特性而心花怒放:操作符的容錯能力,、一項任務(wù)多種實現(xiàn)、標(biāo)點,、正則表達式以及變量機制等,。所有這些都賦予您的手指更靈活的魔力。在某些方面 Perl 的確能給 C 和 Java 程序員很多有用幫助,,可惜目前它還遠達不到眾所周知的程度,。因此,抓緊機會提高您的 Perl 水平吧,! Perl 有時甚至令有經(jīng)驗的程序員也覺得頭疼,,因為他們發(fā)現(xiàn)一不小心就會寫出模棱兩可的語句,。但這種在結(jié)構(gòu)、特性體系方面的模糊性從另一方面顯示了 Perl 語言強大的能力,。畢竟 Perl 語言最開始的設(shè)計初衷就是希望能用多種方式來達到同一目的,。 這里我們將要探討 Perl 5.6 中那些最容易混淆的特性,并將它們與 C/C++/Java 中相應(yīng)的特性做比較,。主要圍繞 "Natural Language Principles in Perl" 中的原則展開(Larry Wall 著,,參看本文末尾的 資料 部分),因為它們是 Perl 最能與 C,、C++ 和 Java 語言區(qū)分開的地方,。此外,關(guān)于 Perl 語法的結(jié)構(gòu)可以在 "perldoc perlsyn" 參考文檔中找到,,另一本值得推薦的 Perl 指南讀物則是 Programming Perl,。 Perl 解釋器 從最簡單的 Perl 程序入手
可以看到 Perl 解釋器運行了這個只有一行的腳本程序,并輸入該表達式的計算結(jié)果 11,。 Perl 解釋器有許多選項。例如,,-e 選項表示將命令行的輸入作為腳本文件來執(zhí)行,,因此上面的腳本例子也可以這樣實現(xiàn):在命令行輸入 速度和 Benchmark 要記住,,過早的優(yōu)化往往是錯誤的根源。如果您在 Perl 中寫了一個原型,,并用其他語言來重寫是沒有問題的,。原型意味著能夠方便地開發(fā)。 與 Java 相比而言,,Perl 也能夠很好地工作,。Perl 不象 Java 那樣擅長于線程,但它的 Tk GUI 界面工具箱卻比 Java 的 Swing GUI 庫要好,。并且 Java 代碼總是能夠連接到 Perl 程序中,,反之亦然。因此,,有時您可以通過某種程度上的結(jié)合,,使得程序在兩方面都做得很好。 異常,、編譯和文檔 事實上,,Perl 程序在運行之前還是需要編譯的,只是和 C/C++/Java 的編譯方式不相同,。在設(shè)計和效果上,,它和 Java 的字節(jié)解釋過程很相似。關(guān)于編譯的更詳盡內(nèi)容,,可以參閱 "perldoc perlrun" 和 "perldoc perlcc" 文檔,。 可用 POD 格式來將文檔嵌入 Perl 程序。這種文檔嵌入方式比 Javadoc 格式(僅適合于 API 文檔)要通用,但比不上 C/C++/Java 的注釋,。 即使和 C,、C++ 或 Java 比較起來,Perl 程序也不算是一種結(jié)構(gòu)性強的語言,。例如,,BEGIN 語句塊會被首先執(zhí)行,但它可以在程序中多次說明,。定義,、變量和函數(shù)體可以在程序的任意位置出現(xiàn),Perl 所提供地強大功能可以最好地滿足這種隨意性,。 由于這種松散結(jié)構(gòu),、嵌入的注釋、以及為追求方便而導(dǎo)致的令人混淆的語句,,使得書寫 Perl 程序更像是在寫一封英文信件,。 Perl 的容錯能力 語句之間或函數(shù)參數(shù)之間的分隔符
Perl 能盡最大可能地消除這些語句可能引起的歧義,。當(dāng)然,,有些時候仍有無法解決的歧義(在這一點上,Perl 就象英語一樣),。 Perl 中另一個容易引起歧義的地方在于:變量經(jīng)常會被隱含使用,。例如,"print" 語句缺省時會打印 $_ 變量的值,。在其他一些含混的語句操作中,,$_ 變量也是它們的缺省值,這就造成了一種混亂,。例如: 隱含使用變量
這里你可以看到,,使用缺省變量能讓編程方便簡潔。也就是說,,Perl 和英語類似,,通過某種模糊性來簡化表達式。 一項任務(wù)多種實現(xiàn) 但對于同一件事情,,Perl 至少有兩種解決方法,。TMTOWTDI 原則就是 Perl 的座右銘,各種處理上的差異在 Perl 編程中是深受鼓勵的,。 下面來看一個打印數(shù)組元素的例子,。所有的表達方法都達到同一目的,。 打印數(shù)組元素
要理解以上這些代碼的唯一途徑就是掌握所有 Perl 的語法。不要擔(dān)心哪種方法才是正確的,,因為實現(xiàn)同一目標(biāo)總是有多種正確的方法,。考慮這些不同的表達方式,,您可以更深刻體會 Perl 的這個座右銘,。 另外,雖然一個任務(wù)的實現(xiàn)有多種方法,,但這并不意味著所有方法都是正確的,。通常情況下,更有可能寫出的是一些錯誤代碼,。為了保證代碼的正確性,,最好盡量使用那些 Perl 內(nèi)置的函數(shù),而較少使用自己所編寫的函數(shù),,并且注意證明并記錄這些不那么顯然的方法,。 正則表達式 Perl 的正則表達式是從 shell 腳本程序以及 awk/grep 工具中繼承而來的,。但它的能力卻遠遠超出了原來的模型,。 基礎(chǔ)的正則表達式是非常容易書寫的,但難以讀懂,。例如 "con\w+" 和 "contra",、"contrary" 匹配,但與 "pro" 或 "con" 都不匹配,。然而在 Perl 5.6.0 中,,正則表達式被固化了。Unicode 字符集,、模式內(nèi)任意代碼操作,、flag toggles、條件表達式以及其他特征都被添加到正則表達式庫中,。 對于初學(xué)者的一個最好的建議就是:首先學(xué)習(xí)最基本的正則表達式(參閱 資料 部分,,或 "perldoc perlre" 參考手冊),稍后才進一步學(xué)習(xí)那些復(fù)雜的高級特性,。由于正則表達式必須全寫在一起,,中間沒有辦法添加注釋,這就讓它們成為所有 Perl 代碼中最難讀懂的一部分。因此建議大家書寫已成型的代碼,。 在 C/C++/Java 中正則表達式屬于外部的函數(shù)包,,但 Perl 是目前最佳的正則表達式搜索和代換工具。在極少數(shù)情況下,,它可能會比純 C 程序慢一些,,但對于那些純粹面向正則表達式的問題,Perl 依然是您首選的工具,。 標(biāo)量,、數(shù)組和哈希散列 筆者推薦使用 "use strict"。通過它可以保證變量在使用之前聲明,,從而避免打字錯誤等引起的程序錯誤,。 如果沒有做到這一點,則有可能遇到下面這樣的問題: 一個常見的打字錯誤
此例子中,,程序員本來要打印變量 i 的值,,結(jié)果卻敲成了 j。Perl 并不會覺得這段代碼有什么問題,,它會繼續(xù)執(zhí)行打印語句,,顯示 $j 的值即什么都沒有。有些時候,,Perl 的自動生成對象的確很有用,,但以我的經(jīng)驗來說,最好還是用 "use strict" 來關(guān)掉這一自動功能,,從而避免上述問題,。 Perl 變量可以是標(biāo)量 (scalars)、數(shù)組 (arrays)或哈希散列 (hashes,,又叫做關(guān)聯(lián)數(shù)組),。(事實上,Perl 中有多種數(shù)據(jù)類型,,但是程序員并不會直接面對它們,。)此外也可以是引用,通常它們也是一種標(biāo)量類型,。其中標(biāo)量名稱以 "$" 開頭,,數(shù)組名以 "@" 開頭,,而散列則以 "%" 開頭。 標(biāo)量是 Perl 中最簡單的數(shù)據(jù)類型,。每個標(biāo)量都有唯一的值,或者是字符串或者是引用,。在必要的時候,,字符串和數(shù)字可以互相轉(zhuǎn)化。這常讓初學(xué)者覺得欣喜異常,??匆幌逻@個例子: 標(biāo)量
其中標(biāo)量 $i 的值是字符串 "hi there",它對應(yīng)的數(shù)值為 0,。因此 1 + "hi there" 的值為 1,,程序運行結(jié)果為 1。 不過這并不意味著 Perl 解釋器在對某個標(biāo)量分別考慮其字符串類型和數(shù)字類型,。事實上,,在內(nèi)存中只是一個含有某個值的標(biāo)量。如果在數(shù)值運算的語句中(如加法),,這個標(biāo)量值就轉(zhuǎn)化成數(shù)值形式,;如果在字符串操作語句中(例如打印),,則以字符串形式執(zhí)行,。但無論以什么形式運算,該標(biāo)量變量實質(zhì)上只有一個值,。 未定義的標(biāo)量的值為 "undef",。如果在 C/C++/Java 程序中,您可以將其他值與 null 比較,,但在 Perl 中卻不能拿任何東西來與 "undef" 做比較,。可用這樣使用 defined() 函數(shù): Use of the ‘defined()‘ function
數(shù)組實質(zhì)上就是一組標(biāo)量,。如果需要,,數(shù)組大小可以自動改變,有點象 Java 中的 Vector 類,。C 和 C++ 中沒有與 Perl 數(shù)組類型相當(dāng)?shù)臇|西,,但它們也有一些提供類似功能的庫(如 STL)。數(shù)組的一個有趣特性在于,,數(shù)組的標(biāo)量數(shù)值等于它的元素個數(shù): 數(shù)組中的元素個數(shù)
散列與數(shù)組類似,,但里面的標(biāo)量并不是按照位置排序的,而是通過另一個標(biāo)量(必須是唯一值)來進行索引,。例如一個用 social security number 作索引的名字列表就是一個散列,。將某個鍵值插入到散列后,,該散列會自動擴展。散列與 Java 中的 HashMap 和 Hashtable 類很相似,。 引用類型其實也是標(biāo)量,,它們類似于 C 語言中的指針,能夠指向任何東西,。這就允許 Perl 生成一個散列的數(shù)組,、數(shù)組的散列、散列的散列,、或是數(shù)組的數(shù)組(多維數(shù)組),。有多種方法來獲得引用所指向的內(nèi)容,或者直接使用引用的名字,、或者使用 "->" 操作符,。引用是一個涉及范圍非常廣的問題,可以參考 "perldoc perlref" 參考文檔來獲得更多相關(guān)信息,。 C 和 C++ 只有一些固定類型的標(biāo)量,。當(dāng)程序員要使用數(shù)組或哈希散列時,不得不去使用鉤子 (hoop) 或是 STL 等外部庫,。 Java 中有相當(dāng)于 Perl 里數(shù)組或散列功能的類庫,,但它們在 Java 語言中并不是那么直接。比如說要對散列上所有元素做操作所需要的時間大約是 Perl 的三倍,。 對散列中所有元素進行操作的 Java 代碼
對散列中所有元素進行操作的 Perl 代碼
Perl 的缺憾 由于 Perl 程序能夠被連接到 C 的庫中(事實上,,這也就是 Perl 應(yīng)用廣泛的原因之一),,這就使得幾乎沒有任何 C 或 C++ 能做而 Perl 不能的事情。 與 C 和 C++ 相比而言,,Perl 有時欠缺的是運行速度,。這的確是一個問題,但是通過良好的編程算法以及 Perl 內(nèi)置函數(shù)的使用,,就能夠克服這一缺點,。 Perl 還不能直接使用 C 和 C++ 的庫。必須通過不同的模塊和綁定,,才能夠?qū)⑦@些庫中的常量以及函數(shù)功能轉(zhuǎn)化成適應(yīng) Perl 的樣子,。這就會導(dǎo)致開發(fā)和程序運行的速度降低,。但由于 CPAN 庫中發(fā)布了大量這些方面的模塊,因此這個問題并不是那么難以解決,, 在訓(xùn)練編程技巧方面,,Perl 并不象 C 和 C++ 那樣深入人心。它是一門年輕的語言,,雖然很受歡迎,,但并未被人們普遍接受。然而,,大多數(shù)的 UNIX 系統(tǒng)上都安裝了 Perl,且其他的操作系統(tǒng)也都支持 Perl,。 Perl 支持單一繼承或多繼承,、封裝以及多態(tài),但這僅僅是通過外部模塊或程序員的協(xié)同來實現(xiàn)的,。也就是說,,Perl 語言本身并沒有嚴(yán)格的面向?qū)ο缶幊桃?guī)則,需要程序員自己來實現(xiàn)面向?qū)ο?。這一點有好也有壞,,這就要取決于程序員或項目本身了。 Perl 的線程以及統(tǒng)一字符編碼(Unicode)支持遠遠落后于 Java,,也稍微次于 C/C++,。Java 從最開始設(shè)計就支持線程和 Unicode,而 C/C++ 則比 Perl 擁有更多的時間來調(diào)整這方面的正確支持,。在 Perl 中,,對線程和 Unicode 的支持仍處于起步階段,但 5.6.0 之后的穩(wěn)定版本發(fā)布之后這一點將得到改觀,。 Perl 的優(yōu)勢 Perl 并不適合于所有人,。它需要讀者去適應(yīng),,卻接受它的所有缺點和優(yōu)點。我們并不是覺得 Perl 酷才采用它,,而是因為它的確是一種非常好的工具,。如果在解決某個問題時使用其他語言更合適,,那么就應(yīng)該放棄 Perl。一個好程序員的手頭總是有好幾種有用的工具,。 Perl 有一些小的不足,,但那些不知疲憊的程序員會忽略掉這些缺點。如果的確需要線程和 Unicode 支持,,或是嚴(yán)格的面向?qū)ο缶幊?,那么你只好根?jù)這些需要來選擇其他更合適的語言了。 Perl 是一門通用的靈活的語言,,可以象膠水一樣將其他許多不同的模型粘合起來,。它能夠?qū)崿F(xiàn)任何過程或函數(shù)的算法。通常情況下,,Perl 會大大減少開發(fā)的時間,,因為它對某些常見的任務(wù)(例如對散列表中的所有元素做操作)只需要少量的代碼。最重要的是,,Perl 編程總是相當(dāng)于一個有趣的學(xué)習(xí)過程,。 Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=17168 |
|