第11章:引用
- 引用一律聲明為標量類型(即$開頭的命名變數),使用\運算子取引用
- 對引用變數的修改等同於對引用指向實際資料的修改
- 取變數引用:my $scalar_r = \$scalar;
- 取列表的引用:my $array_r = \@array;
- 取雜湊的引用:my $hash_r = \%hash;
my @array1 = (10, 20, 30, 40, 50); my @array2 = ( 1, 2, \@array1, 3, 4); |
- 因為\@array1本質上只是一個標量,所以列表不會被扁平化,依舊保留了嵌套層次
- 對匿名列表的引用:將列表的()替換為[]:my $array_r = [1, 2, 3, 4, 5];
- 對匿名雜湊的引用:將雜湊的()替換為{}:my $hash_r = { apple => "pomme", pear => "poire" };
- 對引用變數解引用通過{$var_r}實現
my @array = (1, 2, 3, 4, 5); my $array_r = \@array; my @array2 = @{$array_r}; #拷貝了數組 |
-
- 對於數組引用,可以將${$ref}簡記為$ref->,例如可以將${$ref}[2]簡記為$ref->[2],而將${${ref}[2]}[1]簡記為$ref->[2]->[1],並進一步簡記為$ref->[2][1]
- 使用undef銷毀一個引用:undef $ref; perl對於引用指向的資料維護一個引用計數,當計數減到0時引用指向的資料被銷毀,記憶體空間被釋放
- 使用引用使得表示複雜的資料結構成為可能。這些資料結構包括矩陣(多維陣列)、鏈表、樹、圖等。
- 一些思考:C/C++的引用主要為了傳地址。與C/C++中的指標、引用不同的是,perl引用除了傳遞地址外,還是perl中將標量(scalar)、列表(list)、雜湊(hash)進行一般化(或者說統一)表示的機制,使用引用後,可以將標量、列表、雜湊均表示為標量(因為地址本質上是一個無符號整型數,這一點與C中的void*有些類似)。通過引用,就可以解決perl中無法儲存帶有嵌套層次的列表、無法表示複雜資料結構的問題。
第12章:模組
- 模組是一個perl原始碼檔案,與普通的.pl原始碼檔案相比,模組有如下兩個不同點:
- 副檔名不是pl,而是pm(這一條並不是強制條件)
- 最後有一句1;(或是return true;或是任何返回true值的語句)強制要求
- do用於在perl代碼中的任意位置嵌入一個.pl指令碼或是.pm模組,文法是do “filename”;perl將會在@INC中的路徑下尋找filename
- 如果在main.pl中執行do ‘inc.pl’;需要注意的是inc.pl中的代碼不能訪問main.pl中定義的lexical變數
- require用於在perl代碼中的任意位置嵌入一個模組,文法是require “filename”;或require modulename;當使用require modulename;時,modulename形如module::submodule::subsubmodule,代表檔案./module/submodule/subsubmodule.pm
- 與do不同,require只支援嵌入模組,因此require的檔案必須以返回true語句結束
- 對於一個檔案只嵌入一次,即使寫了多條對同一檔案的require語句
- use用於在perl代碼中的任一位置嵌入一個模組,文法是use modulename;modulename同require中的modulename
- 與require不同,use在編譯前執行,也就是說,即使use中的模組寫在代碼中的最後一句,也會第一個執行
- |
do |
require |
use |
支援 |
源碼 模組 |
模組 |
模組 |
文法 |
do ‘filename’; |
require ‘filename’; require modulename; |
use modulename; |
處理次數 |
嵌入幾次,處理幾次 |
僅一次 |
僅一次 |
檔案不存在 |
跳過,不報錯 |
報錯 |
報錯 |
執行時間 |
運行時 |
運行時 |
編譯時間(最先處理) |
- @INC變數也是一個普通perl列表,可以更改,因此可以自行向其中添加路徑(使用unshift或者push)
- 模組檔案的開頭應該聲明package packagename;packagename形如package::subpackage::subsubpackage,對於package::subpackage::subsubpackage中的函數sub1,調用方法是package::subpackage::subsubpackage::sub1
- 注意package聲明的是模組名,而require和use使用的模組名實際上是路徑名,和模組名並不一樣,比如說有一個模組為./m1/test1.pm,其中的package聲明卻是package m1::test2;該包內包含一個函數sub1,則在main.pl中應該use/require m1::test1;而在調用時則應該寫m1::test2::sub1()
- 當然,為了清晰、易管理,模組名和模組檔案名稱、路徑應該保持一致
- 可以使用Exporter類簡化包內的函數調用寫法。沒有使用Exporter時,必須寫形如package::subpackage::subsubpackage::sub1的調用,過於囉嗦,而在寫包時繼承Exporter即可:
package Acme::Webserver::LoggerExporter; # Acme/Webserver/LoggerExporter.pm use strict; use warnings; # become an exporter and export the functions use Exporter; use base 'Exporter'; our @EXPORT = qw(open_log close_log write_log log_level); 則調用open_log時就可以將全寫調用: Acme::Webserver::LoggerExporter::open_log() 改為簡寫調用: open_log() |
use Data::Dumper qw(Dumper); # 可以直接調用Dumper() |
幾個常用的包
- Data::Dumper是將變數序列化為perl文法的字串的包,序列化列表和雜湊時非常方便
- File::Find是一個遍曆檔案夾,對其中每一個檔案進行處理的函數,用法是File::Find::find(\&wanted, “/home/simon/”);
- 首個參數wanted是一個回呼函數,對每個檔案應用。第二個參數是執行檔案夾
- 每次執行回調時目前的目錄被切換為當前檔案所在的目錄
- 目前的目錄的相對路徑為$File::Find::dir
- $_為當前檔案的檔案名稱
- $File::Find::name為當前檔案的全名(包括目錄)
- Getopt::Std和Getopt::Long是兩個處理命令列參數的包,可以將形如-al的簡寫命令列參數解析為a和l兩個參數,也可以將-a arg1 -l arg2這樣的命令列參數解析為雜湊映射
- File::Spec是一個處理路徑字串的包,包括路徑字串簡化、路徑疊加、路徑解析等
- Benchmark是一個效能測試包,可將某一代碼塊重複執行若干次,測得績效參數
- Win32是一個封裝了一些Win32 API的包,包括Win32::Sound、Win32::TieRegistry等
第13章:物件導向的Perl
- perl中並沒有真正的“類”,所謂的類,其實是一個包
- 要定義一個類,聲明一個package即可:package Person;
- 類的建構函式固定取名為new,即sub new {...}
- 初始化類對象通過$obj = new Person();或者$obj = Person->new();
- 建構函式sub new要點:
- 參數表的第一個參數(@_[0])是類名,第二個開始為調用建構函式時傳入的參數
- 通過傳入雜湊實作類別似成員變數的功能
- 產生對象引用後,必須使用bless()函數對引用的類型進行轉換
- 最後一句必須返回產生的對象引用
#類定義 package Person;sub new { #此時_@為(‘Person’, ‘name’, ‘Carl’, ‘gender’, ‘male’) $classname = shift; #獲得類名,此時$classname為’Person’, _@為(‘name’, ‘Carl’, ‘gender’, ‘male’) my $self = {@_}; #將傳入參數轉化為雜湊,$self為(‘name’=>’Carl’, ‘gender’=>’male’) bless $self, $classname #將引用$self轉化為$classname類型 return $self; #返回的Person對象本身是一個雜湊,含有所有成員變數 } #類使用 $person = Person->new(‘name’=>’Carl’, ‘gender’=>’male’); |
- Package內定義的變數為類變數,即靜態成員變數(static member variable),不能直接存取,必須定義訪問器(accessor, 即get/set函數)
- 成員函數要點:
- 名字以底線_開頭的成員函數為私人的
- 函數的第一個參數(即@_[0])為對象引用,第二個參數開始為函數參數
#類定義 package Person;sub new {...} #省略 sub _init {...} #私人函數 sub name { my $self = shift; #取調用對象引用 my $name = shift; #取第二個參數 $self->{name} = $name if defined $name; #如果傳入名字,則設定名字為傳入值 return $self->{name}; #返回名字值 } #類使用 $person = Person->new(‘name’=>’Carl’, ‘gender’=>’male’); $person->name(‘Caesar’); #將名字設定為’Caesar’ print $person->name(), “\n”; #列印名字,將列印’Caesar’ |
後記
Perl給我留下深刻印象的地方:
- 簡捷易用的文本I/O、Regex使Perl成為文本處理的利器
- 提供眾多UNIX API,加上指令碼語言的靈活性,Perl適合進行UNIX系統管理
個人感覺Perl中的兩個痛點,也是Perl的敗筆:
- 引用(Reference)
- 列表自動一維化的機制莫名其妙。Perl中標量、列表和雜湊擁有各自不同的詞法標識($、@、%,列表、雜湊內容均使用(),列表取值使用[],雜湊使用{}),將其引用化後解引用又有一套各自不同的詞法,很容易弄暈
-
- Perl中的OO機制有點半殘,單單是建構函式中必寫的幾行:
my $classname = shift; my $self = {@_}; bless $self, $classname; return $self; |
-
- 以及函數中第一句必寫的my $self = shift;就讓人十分討厭,重複性勞動。如果這真的是一門OO語言,這些工作應該由編譯器完成。
- 根據Wikipedia上Perl頁面的介紹,OO是Perl 5中加入的新特性,這說明Perl最早並沒有被設計為一門OO程式設計語言,所以Perl 5中的OO特性可以看成是在過程式語言中進行的一種升級。比如每個函數第一句就必有的my $self = shift;就與C++中的this指標神似,只不過C++作為一種新語言革命得比較徹底,this指標是由編譯器自動提供的,不必手工擷取。與C++相比,Perl裡的OO更像是用C語言實現的OO,說到這裡,有空可以去看看《Object-oriented Programming with ANSI-C》,這本書講了用C語言實現OO特性的各個技術細節,“通過這本書你可以明白C++, Java, Python 等物件導向語言中的類、繼承、執行個體、串連、方法、對象、多態... 都是如何?的. 能讓你通過C來寫出優美並可以重用的代碼.”(以上文字來自豆瓣網友Border