Perl 哲學
Perl 是一種能“干實事”的語言灯荧。它靈活僵井、寬容陕截、可塑。在一名編程能者的手中批什,它可以 完成幾乎所有的任務农曲,從“一句話”的簡單運算和自動完成任務到多人、多年的項目驻债, 外加夾在兩者之間的部分乳规。
Perl 功能強大,摩登 Perl────一個集最上乘知識和經(jīng)驗合呐,以及來自全球 Perl 社區(qū)的 可重用慣用語的Perl────它可維護暮的、快速、易用淌实。也許最重要的是冻辩,它能夠幫助你無挫折、 無繁文縟節(jié)地做你需要做的事拆祈。
Perl 是一門實用主義語言恨闪。你,程序員放坏,完全地掌控著自己編寫的程序咙咽。相對于操控你的思想 和你面對的問題來適應語言設計者認為你應該怎樣寫程序,Perl 允許你按你覺得合適的方式 解決問題轻姿。
Perl 是一門伴隨你成長的語言犁珠。你能夠用你在一小時內(nèi)閱讀本書所學到的知識寫出有用的程序。 如果你還能花點時間理解語法互亮、語義、語言設計背后的哲學余素,你會變得更有成效豹休。
首先,你需要知道如何學到更多東西桨吊。
Perldoc
Perl 最有用也是最不受感恩的功能之一就是perldoc實用工具威根。這個程序是每一個完整 Perl 5 安裝的一部分。(footnote: 在免費的 GNU/Linux 的發(fā)行版或是其它類 Unix 系統(tǒng)下视乐,你也許 需要安裝一項額外的軟件包洛搀,Debian 和 Ubuntu 上為perl-doc)。它能顯示系統(tǒng)上每一個 已安裝 Perl 模塊的文檔────無論是核心模塊佑淀,或是那些來自 Comprehensive Perl Archive Network (Perl 綜合典藏網(wǎng)留美,CPAN) 的部分────還包括上千頁 Perl 核心文檔。
如果你傾向于一份在線版本,http://perldoc.perl.org/存有 Perl 文檔的最近版本谎砾。http://search.cpan.org/可顯示 CPAN 上所有模塊的文檔逢倍。對于 Windows 用戶,ActivePerl 和 Strawberry Perl 都在開始菜單提供了指向這份文檔的鏈接景图。
perldoc的默認行為是顯示某模塊的文檔或是 Perl 核心文檔的特定章節(jié):
$perldoc List::Util
$perldoc perltoc
$perldoc Moose::Manual
第一個例子解開為List::Util模塊所編寫的文檔并按適合你的顯示屏的方式顯示出來较雕。 CPAN 模塊 (CPAN) 的社區(qū)標準建議額外的庫使用和核心模塊一致的格式,使得閱讀諸如Data::Dumper核心模塊以及從 CPAN 安裝的其他模塊的文檔并無差別挚币。標準文檔模板包 含了一份對模塊的描述亮蒋,用法演示,和后續(xù)對模塊的詳細解釋和接口介紹妆毕。雖然文檔的長度 因作者而不同宛蚓,其形式卻相當統(tǒng)一。
第二個例子顯示一個純粹的文檔文件设塔,此處是核心文檔自己的目錄凄吏。這個文件描述了核心文 檔的每一個獨立的部分。瀏覽一下闰蛔,簡單地看看 Perl 的能力痕钢。
第三個例子是第二個例子的翻版。Moose::Manual是 Moose CPAN 發(fā)行版 (Moose) 的一部分序六。它同樣也是一個純文檔任连,不包含任何代碼。
類似的例诀,perldoc perlfaq將會顯示 Perl 5 常見問題的目錄随抠。僅掃一眼這些問題也會使 人獲益匪淺。
perldoc實用工具還有不少能力(參見perldoc perldoc)繁涂。其中兩個最有用的是-q以及-f參數(shù)拱她。-q參數(shù)讀取一個或多個關鍵字,在 Perl FAQ 中查找扔罪,并顯示所有結 果秉沼。從而perldoc -q sort返回三個常見問題:我如何按(任何條件)排序數(shù)組?矿酵,我如何將一個哈希排序(可選的附加條件:通過值而非鍵)唬复?,以及我如何使一個哈先梗總保持在有序狀態(tài)敞咧?.
-f參數(shù)顯示核心文檔中某 Perl 內(nèi)置函數(shù)的相應部分。perldoc -f sort解釋了sort操作符的行為辜腺。如果你不知道你要查找函數(shù)的名稱休建,perldoc perlfunc可以給出一張函數(shù)列表乍恐。
perldoc perlop和perldoc perlsyn記錄了 Perl 中符號形式的操作符和語法結構。perldoc perldiag解釋了 Perl 警告消息的含義丰包。
Perl 5 的文檔系統(tǒng)是POD禁熏,或作Plain Old Documentation。perldoc perlpod描述 了 POD 的工作方式邑彪。perldoc實用工具能顯示你為你的項目創(chuàng)建和安裝的所有 Perl 模塊中 的 POD瞧毙,其他諸如podchecker等 POD 工具,可以驗證你的 POD 的格式寄症,Pod::Webserver宙彪, 可以通過一個小型 Web 服務器以 HTML 的形式顯示本地的 POD,同樣也將驗證其正確性有巧。
perldoc還有其他用途释漆。給出-l命令行參數(shù),它將顯示文檔文件的路徑而非文件內(nèi)容篮迎。(footnote: 注意:一個模塊除.pm文件外可能還有多個分離的.pod文件)男图。給出-m參數(shù),它 將顯示整個模塊的內(nèi)容甜橱,包括代碼逊笆,并不對任何 POD 指令進行處理。
表達力
在 Larry Wall 創(chuàng)造 Perl 之前岂傲,他研究的是語言學和人類語言难裆。他的經(jīng)歷持續(xù)地影響著 Perl 的設計。因項目的風格镊掖、編寫程序的可用時間乃戈、所期望的維護代價,甚至是你 個人的表達能力的不同亩进,編寫一個 Perl 程序的方法是多種多樣的症虑。你可以采用直截了當 、自頂而下的風格镐侯。你可能編寫很多小而獨立的函數(shù)侦讨。你可能會用類和對象對你的問題建模。 你也可以避讓或擁抱 Perl 的高級功能苟翻。
Perl 黑客們對此有句口號,TIMTOWTDI骗污,發(fā)音為“Tim Toady”, 或作 "There's more than one way to do it!"
這種表達力提供了一個大型調(diào)色盤崇猫,用它巨匠可以創(chuàng)造出驚人的、宏大的建筑物需忿,僅將各 式技巧不明智地堆砌起來只能阻礙代碼的可維護性和易讀性诅炉。你可以寫出優(yōu)秀或者一塌糊 涂的代碼蜡歹。選擇權在你這里。(footnote: ……但是如果你必須弄得一塌糊涂涕烧,那么對其他人好一點)月而。
其他語言也許會建議說,按一條強制的指導思想走每一步路才是解決問題的正途议纯。Perl 允許 你針對最重要的標準進行優(yōu)化父款。在問題的范圍之內(nèi),你可以選擇若干合適的方法────但是注 意一下可讀性和未來的可維護性瞻凤。
作為一個 Perl 的新手憨攒,你可能會覺得某些結構難易理解。Perl 社區(qū)已經(jīng)總結并推出了 不少強大的慣用語 (慣用語)阀参。別指望一下就能理解它們肝集。Perl 的一些特性以微妙的方 式互相聯(lián)系著。
學習 Perl 就好像在學習一門第二或第三口語蛛壳。你會學到一些單詞杏瞻,然后將它們串起來構 成句子,最終能夠進行一小段簡單的對話衙荐。熟能生巧捞挥,讀也一樣,寫也一樣赫模。你不用一次性 理解本章的所有細節(jié)就能夠寫出卓有成效的 Perl 程序树肃。當你閱讀本書余下部分時,請記住 這些原則瀑罗。
另一個 Perl 的設計目標是胸嘴,盡可能不使有經(jīng)驗的 (Perl) 程序員吃驚。舉例來說斩祭,用一個 數(shù)值操作符將兩個標量相加($first_num + $second_num) 很顯然是一次數(shù)值的操作劣像,該 操作符必須按數(shù)值對待這兩個標量來產(chǎn)生數(shù)值結果。無論$first_num和$second_num中的內(nèi)容是什么摧玫,也不必用戶或程序員手動操作耳奕,Perl 會將它們強制轉為數(shù)值(數(shù)值強制轉換) 。 你已經(jīng)通過選擇數(shù)值操作符 (數(shù)值操作符)表達了你想將它們用作數(shù)值的意圖诬像,因 此 Perl 很高興地替你處理后續(xù)工作屋群。
一般而言,Perl 程序員可以期望 Perl 能完成他們想要 Perl 完成的事坏挠,這便是DWIM--do what I mean的意思芍躏。你也能見到另一種提法最小驚奇原則。對 Perl 有個粗略的 了解之后(特別是它的上下文)降狠,在讀到一個不熟悉的 Perl 表達式時对竣,就 有可能猜出它的意圖庇楞。
如果你剛接觸 Perl,你將漸漸培養(yǎng)出這種技能否纬。Perl 表達力的反面就是吕晌,新手在學全 Perl 強大功能之前就可以寫出有用的程序。Perl 社區(qū)通常將此稱作baby Perl临燃。雖然它可能聽 上去有些輕蔑睛驳,但請千萬別生氣,每個人都是這樣過來的谬俄。抓住向更有經(jīng)驗程序員學習的機會柏靶, 并對你不理解的慣用語和結構(向他們)索求解釋。
一個 Perl 新手可能會按下面的方法把列表中所有的元素乘以三:
my @tripled;
my $count = @numbers;
for (my $i = 0; $i < $count; $i++)
{
? ? $tripled[$i] = $numbers[$i] * 3;
}
一個 Perl 內(nèi)行可能會這樣寫:
my @tripled;
for my $num (@numbers)
{
? ? push @tripled, $num * 3;
}
一個經(jīng)驗豐富的 Perl 黑客可能會這樣寫:
my @tripled = map { $_ * 3 } @numbers;
編寫 Perl 程序的經(jīng)驗將會幫助你專注于要做什么而非怎么做溃论。
Perl 是一門有意隨著你對編程理解程度的增長而成長的語言屎蜓。它不會因寫出簡單的程序而懲罰你。 它允許你為了直接明了钥勋、富表達力炬转、代碼重用和可維護性來完善、擴展你的程序算灸。好好利用這條哲學扼劈。 出色地完成你的任務比寫出概念上純美的程序更為重要。
本書余下部分展示如何按對你有利的方式使用 Perl菲驴。
上下文
[當閱讀這個小節(jié)的時候荐吵,我意識到 Perl 中的上下文的基礎是“用什么操作符”以及“在哪里用它”。 在本小節(jié)和 operator_types.pod 中赊瞬,你決不會直接地說出來先煎。但這是真的。我試圖在這個小節(jié)的 某處中直接地說出來巧涧,但是我還無法決定在哪里已經(jīng)怎樣說薯蝎,因此這只是一個提議。] 口語中有上下文的概念谤绳,即某詞匯或短語的正確用法和含義由語境所決定占锯。理解一下在口語中的情況, “Please give me one hamburgers!” 中不合適的復數(shù)用法(footnote: 名詞的復數(shù)形式和數(shù)量不符)缩筛, 聽上去就不對消略,或者 “l(fā)a gato” 中不正確的性別(footnote: 冠詞為陰性,但是名稱為陽性)使得母語人士輕聲竊笑瞎抛。 同樣考慮代詞 “you” 或者名詞 “sheep” 疑俭,是單數(shù)是復數(shù)還得由句子的余下部分決定。
Perl 中的上下文是類似的婿失,這門語言理解對數(shù)據(jù)數(shù)量上的期望同時也知道應該提供什么樣的數(shù)據(jù)钞艇。 Perl 將高興地嘗試提供給你恰好合你心意的數(shù)據(jù)。
Perl 中上下文的每一種類型豪硅,都和你所需某操作符結果的個數(shù)(零個哩照、一個、或許多個)相對應懒浮, 同一個操作符在(不同的上下文中)有著不同的行為飘弧。在 Perl 中,如果你要求:“給我零個結果砚著, 我不在乎有沒有”或是“給我一個結果”再或是“給我多個結果”次伶,那么一個特定的結構按這些不同的要 求做不同的事情是完全可能的。
同樣稽穆,特定上下文將你所需明朗化:是數(shù)值冠王,是字符串值、或是一個為真或假的值舌镶。
如果你打算把 Perl 代碼當成一系列獨立于環(huán)境的單一表達式來讀寫柱彻,那么上下文將會變得十足狡猾。 在一次長時間的調(diào)試后餐胀,你也許會一拍腦門哟楷,發(fā)現(xiàn)你對程序上下文的假設是錯誤的。然而在對上下文 了如指掌后否灾,它們會使你的代碼更清晰卖擅、更簡潔、更靈活墨技。
空惩阶、標量和列表上下文
上下文之一掌控著你期望事物的多少。這就是數(shù)量上下文健提。這個上下文和英語中“主謂一致” 相當琳猫。即使尚未了解這條規(guī)則的正式定義,你很有可能理解句子 "Perl are a fun language" 中的錯誤私痹。 Perl 中的規(guī)則是脐嫂,你所要求事物的數(shù)量決定了你得到的。
假設你有一個稱為some_expensive_operation()的函數(shù) (函數(shù))紊遵,它進行一昂貴的計算并 產(chǎn)生許多許多結果账千。如果你直接調(diào)用該函數(shù)且對返回值不加利用,那么就稱你在空上下文中調(diào)用了 這個函數(shù):
some_expensive_operation();
將函數(shù)的返回值賦值給單個元素使得函數(shù)在標量上下文中求值:
my $single_result = some_expensive_operation();
將調(diào)用函數(shù)的結果賦值給一個數(shù)組(數(shù)組)或是列表暗膜,或者在一個列表中使用該結果匀奏,使 得函數(shù)在列表上下文中求值:
my @all_results? ? ? ? = some_expensive_operation();
my ($single_element)? = some_expensive_operation();
process_list_of_results( some_expensive_operation() );
前例的第二行可能看上去有些迷惑,這里的括號給了編譯器一點提示:盡管這里只有一個 標量学搜,該賦值應在列表上下文中發(fā)生娃善。在語義上等同于將列表中的第一個元素賦值給一個 標量论衍,并將列表的其余部分賦值給一個臨時數(shù)組,隨即丟棄該數(shù)組聚磺。除非真的發(fā)生(類似于 后例的)數(shù)組賦值:
my ($single_element, @rest) = some_expensive_operation();
為什么對函數(shù)來說上下文是有趣的坯台?假如some_expensive_operation()計算的是按優(yōu)先 級排序過的家務事。如果只有做一件事的時間瘫寝,你可以在標量上下文中調(diào)用它蜒蕾,獲得一項有用的 任務────也許并不一定是最重要的,但是不會是排在最底下的那一項焕阿。在列表上下文中咪啡,該函數(shù) 能完成所有的排序、搜索和比較暮屡,可以給你一份順序合適且詳盡的列表撤摸。如果你想所有事情都做, 但是只有那么些時間栽惶,你可以用一個一兩個元素的列表(來接受該函數(shù)在列表上下文中的返回值)愁溜。
在列表上下文中對函數(shù)或表達式────除賦值外────求值,可能造成迷惑性結果外厂。列表會把列表 上下文散播到其所包含的表達式中冕象。下列對some_expensive_operation()的調(diào)用都發(fā)生于 列表上下文:
process_list_of_results( some_expensive_operation() );
my %results =
(
? ? cheap_operation? ? => $cheap_operation_results,
? ? expensive_operation => some_expensive_operation(), # OOPS!
);
上例expensive_operation位于列表上下文,因為它被賦值到一個哈希中汁蝶,而哈希賦值需要一 鍵值對列表渐扮,導致在哈希賦值內(nèi)的所有表達式在列表上下文中求值。
后一個例子通常使期望該調(diào)用為標量上下文的新手程序員吃驚掖棉。相反墓律,這是列表上下文,因為該上 下文為哈希賦值所強制幔亥。使用scalar操作符可以迫使其在標量上下文求值:
my %results =
(
? ? cheap_operation? ? => $cheap_operation_results,
? ? expensive_operation => scalar some_expensive_operation(),
);
數(shù)值耻讽、字符串及布爾上下文
另一類型的上下文決定了 Perl 如何理解某塊數(shù)據(jù)────不是你要多少數(shù)據(jù),而是數(shù)據(jù)的意義帕棉。 你也許早已覺察到 Perl 可以靈活地指出你所有的是數(shù)字還是字符串并在按需在兩者之間轉換针肥。這就是值上下文,有助解釋 Perl 是如何做這些事情的香伴。不必明確聲明(至少是跟蹤)某變量包含(或產(chǎn) 生自某函數(shù))的數(shù)據(jù)的類型慰枕,作為交換,Perl 提供了特定類型的上下文即纲,由它們告知編譯器在某項 操作期間如何對待一個給定的值具帮。
假設你想比較兩個字符串的內(nèi)容。eq操作符能告訴你這些字符串中是否包含相同的信息:
say "Catastrophic crypto fail!" if $alice eq $bob;
當明明知道字符串內(nèi)容不同,但是比較結果卻是相同時蜂厅,你可能會感到莫名其妙:
my $alice = 'alice';
say "Catastrophic crypto fail!" if $alice == 'Bob';? # 啊呀
eq操作符通過強制字符串上下文匪凡,按字符串對待它的操作數(shù)。==操作符則強制數(shù)值上下文葛峻。 示例代碼出錯的原因在于锹雏,兩個字符串在用作數(shù)字時候的值是0(數(shù)值強制轉換)。
布爾上下文發(fā)生在當你在條件語句中使用某值時术奖。在前面的例子中,if語句在布爾上下文中求出eq和==操作的結果轻绞。
Perl 會盡最大努力將值強制轉換成正確的類型 (強制轉換)采记,并依賴于所用的操作符。一定要針對你所需的 上下文使用正確的操作符政勃。
在極少數(shù)情況下唧龄,沒有合適類型的操作符存在,你也許需要明確地強制上下文奸远。強制數(shù)值上下文既棺,在變量前加零。 強制字符串上下文懒叛,將變量和空字符串拼接起來丸冕。強制布爾上下文,使用雙重否定操作符薛窥。
my $numeric_x =? 0 + $x;? # 強制數(shù)值上下文
my $stringy_x = '' . $x;? # 強制字符串上下文
my $boolean_x =? ? !!$x;? # 強制布爾上下文
大體上說胖烛,相比數(shù)量上下文,類型上下文較易理解和識別诅迷。一旦你理解它們的存在佩番,并知道什么操作符提供什 么上下文 (操作符類型),你很少會因為它們而犯錯罢杉。
隱式理念
像不少口語一樣趟畏,Perl 提供了語言學捷徑。上下文即是這樣一個特性滩租。閱讀代碼的無論是 編譯器還是程序員赋秀,都可以通過現(xiàn)有信息了解到所期望結果的數(shù)量和操作符的類型,而不必 額外添加明確信息來消歧持际。Perl 中也存在其他類似的特性沃琅,包括本質(zhì)上是代詞的默認變量。
默認標量變量
默認標量變量(也稱為話題變量)$_蜘欲,是 Perl 中語言學捷徑的最佳例證益眉。它最顯眼的地方 就是它的“缺席”:當缺少一明確變量時,Perl 中許多內(nèi)置操作是針對$_的內(nèi)容進行的。 你仍可以將$_填入所缺變量的位置郭脂,但是這通常是多此一舉年碘。
舉例來說,chomp操作符移去任何尾隨字符串的換行符序列:
my $uncle = "Bob\n";
say "'$uncle'";
chomp $uncle;
say "'$uncle'";
在沒有指明變量時展鸡,chomp移去$_尾部的換行符屿衅,因此下列兩行代碼是等價的:
chomp $_;
chomp;
$_在 Perl 中的功能等同于漢語中的代詞“它”(英語中的代詞it)。第一行 讀作 “chomp它”莹弊,第二行則讀作“chomp”涤久。當你不指明對什么做 chomp 操作時, Perl 理解你的意思忍弛,Perl 總是 chomp它响迂。
類似的,內(nèi)置函數(shù)say和print在缺少參數(shù)時作用于$_之上:
print;? # 將 $_ 打印到當前所選文件句柄
say;? ? # 將 $_ 打印到當前所選文件句柄
? ? ? ? ? # 外加結尾換行符
Perl 的這套正則表達式裝備 (正則表達式和匹配) 同樣可以在$_上進行匹配细疚、 替換和轉譯操作:
$_ = 'My name is Paquito';
say if /My name is/;
s/Paquito/Paquita/;
tr/A-Z/a-z/;
say;
Perl 中許多標量操作符(包括chr蔗彤、ord、lc疯兼、length然遏、reverse及uc) 在你不提供替代選項時,作用于默認標量變量上吧彪。
Perl 的循環(huán)指令 (循環(huán)語句) 同樣設置$_變量待侵,比如用for遍歷一個列表:
say "#$_" for 1 .. 10;? ?
for (1 .. 10)? ?
?{
? ? ? ? say "#$_";? ??
}
……或者是while:
while ()
{
? ? chomp;
? ? say scalar reverse;
}
……再或者是用map轉換列表:
my @squares = map {$_*$_} 1 .. 10;? ? say for @squares;
……又或者是用grep過濾列表:
say 'Brunch time!' if grep { /pancake mix/ } @pantry;
如果你在用到$_的代碼內(nèi)調(diào)用函數(shù),無論是隱式還是顯式来氧,可能導致$_的值被覆蓋诫给。 相似地,如果你編寫了一個使用$_的函數(shù)啦扬,就有可能攪亂調(diào)用者對$_的利用中狂。Perl 5.10 允許你用my將$_作為詞法變量來聲明,這樣便可以避免上述行為扑毡。明智一點胃榕。
while ()
{
? ? chomp;
? ? # 一例反面教材
? ? my $munged = calculate_value( $_ );
? ? say "Original: $_";
? ? say "Munged? : $munged";
}
在本例子中,若calculate_value()或它偶然調(diào)用了其他函數(shù)導致$_的值改變瞄摊,則在 整一次while循環(huán)中勋又,此值將保持被改后的狀態(tài)。用my來聲明可以避免此類情況:
while (my $_ = )
{
...
}
當然换帜,使用帶有具體名字的變量會比較清晰:
while (my $line = )
{
? ? ...
}
你可以在書面寫作中用到“它”(英語:"it")的地方使用$_: 適度地楔壤、在小且精心圈定的范圍內(nèi)。
默認數(shù)組變量
在 Perl 擁有一個隱式的標量變量的同時惯驼,它也有兩個隱式數(shù)組變量蹲嚣。Perl 通過一個名為@_的數(shù) 組向函數(shù)傳遞參數(shù)递瑰。函數(shù)內(nèi)部處理數(shù)組的操作符 (數(shù)組) 默認影響這個數(shù)組。因此隙畜,以下二例代碼 是等價的:
sub foo
{
? ? ?my $arg = shift;
? ? ?...
}
sub foo_explicit
{
? ? my $arg = shift @_;
? ? ...
}
正如$_對應代詞它抖部,@_對應代詞它們。不同于$_议惰,當你調(diào)用其他函數(shù)時慎颗, Perl 自動地為你局部化@_。數(shù)組操作符shift和pop在沒有提供其他操作數(shù)時作用于@_言询。
在所有函數(shù)之外俯萎,默認數(shù)組變量@ARGV存有傳遞給程序的命令行參數(shù)。在函數(shù)內(nèi)隱式用到@_的 同一批數(shù)組操作符倍试,在函數(shù)外隱式地使用@ARGV讯屈。你不可以將@_用作@ARGV。
ARGV有一個特殊的用法县习。如果你從空文件句柄<>讀入,則 Perl 會將@ARGV中的每一個元素當作 文件的名字而打開谆趾。(如果@ARGV為空躁愿,Perl 會從標準輸入讀取。)這個隱含的@ARGV行為在編寫 短小的程序(例如將輸入逆序輸出的命令行過濾器)時很有用:
while (<>)
{
chomp;
say scalar reverse;
}
為什么用到scalar沪蓬?say對其操作數(shù)施加列表上下文彤钟。而reverse又將它的上下文傳遞給自己的操作數(shù), 在列表上下文中它將它們作為列表對待跷叉,在標量上下文中則看作拼接字符串逸雹。這聽上去有點迷糊,因為的確如此云挟。 Perl 5 應將不同的操作用不同的操作符處理梆砸。
如果你用一列文件作參數(shù)運行這個程序:
$perl reverse_lines.pl encrypted/*.txt
……結果應該會是一系列冗長的輸出。不帶參數(shù)運行時园欣,你可以提供自己的標準輸入:通過管道的方式接通其他程序帖世, 或者直接從鍵盤打字。