標籤:
第十四章 Perl5的包和模組
by flamephoenix
一、require函數
1、require函數和子程式庫
2、用require指定Perl版本
二、包
1、包的定義
2、在包間切換
3、main包
4、包的引用
5、指定無當前包
6、包和子程式
7、用包定義私人資料
8、包和系統變數
9、訪問符號表
三、模組
1、建立模組
2、匯入模組
3、預定義模組
一、require函數
用require函數可以把程式分割成多個檔案並建立函數庫。例如,在myfile.pl中有定義好的Perl函數,可用語句require ("myfile.pl"); 在程式中包含進來。當Perl解譯器看到這一語句,就在內建陣列變數@INC指定的目錄中尋找檔案myfile.pl。如果找到了,該檔案中的語句就被執行,否則程式終止並輸出錯誤資訊:
Can‘t find myfile.pl in @INC
作為子程式調用參數,檔案中最後一個運算式的值成為傳回值,require函數查看其是否為零,若為零則終止。例如myfile.pl最後的語句是:
print ("hello, world!\n");
$var = 0;
因為最後的語句值為零,Perl解譯器輸出下列錯誤資訊並推出:
myfile.pl did not reture true value
可以用簡單變數或數組元素等向require傳遞參數,如:
@reqlist = ("file1.pl", "file2.pl", "file3.pl");
require ($reqlist[$0]);
require ($reqlist[$1]);
require ($reqlist[$2]);
還可以不指定檔案名稱,即:
require;
這時,變數$_的值即作為檔案名稱傳遞給require。
註:如果@INC中有多個目錄中含有同一個檔案,則只有第一個被包含。
1、require函數和子程式庫
用require函數可以建立可用於所有Perl程式的子程式庫,步驟如下:
a、確定存貯子程式庫的目錄
b、將子程式抽取放到單獨的檔案中,將檔案放到子程式庫目錄
c、每個檔案末尾加一句非零值的語句,最簡單的辦法是語句 1;
d、在主程式中用require包含一個或多個所需的檔案。
e、運行主程式時,用 -I 選項指定子程式庫目錄,或者在調用require前將該目錄添加到@INC數組中。
例如:假設目錄/u/perldir中存有你的Perl子程式庫,子程式mysub存貯在檔案mysub.pl中。現在來包含上該檔案:
unshift (@INC, "/u/perldir");
require ("mysub.pl");
對unshift的調用把目錄/u/perldir添加到@INC數組,對require的調用將mysub.pl檔案的內容包含進來作為程式的一部分。
注意:
1、應該使用unshift來向@INC中添加目錄,而不是push。因為push增加到@INC的末尾,則該目錄將被最後搜尋。
2、如果你的庫檔案名稱與/usr/local/lib/perl中的某檔案同名,則不會被包含進來,因為require只包含同名檔案中的第一個。
2、用require指定Perl版本
Perl 5中,可以用require語句來指定程式運行所需的Perl版本。當Perl解譯器看到require後跟著數字時,則只有其版本高於或等於該數字時才運行該程式。例如,下面語句表明只有Perl解譯器為5.001版或更高時才運行該程式:
require 5.001;
二、包
Perl程式把變數和子程式的名稱存貯到符號表中,perl的符號表中名字的集合就稱為包(package)。
1、包的定義
在一個程式中可以定義多個包,每個包有一個單獨的符號表,定義文法為:
package mypack;
此語句定義一個名為mypack的包,從此以後定義的所有變數和子程式的名字都存貯在該包關聯的符號表中,直到遇到另一個package語句為止。
每個符號表有其自己的一組變數、子程式名,各組名字是不相關的,因此可以在不同的包中使用相同的變數名,而代表的是不同的變數。如:
$var = 14;
package mypack;
$var = 6;
第一個語句建立變數$var並存貯在main符號表中,第三個語句建立另一個同名變數$var並存貯在mypack包的符號表中。
2、在包間切換
在程式裡可以隨時在包間來回切換,如:
1: #!/usr/local/bin/perl
2:
3: package pack1;
4: $var = 26;
5: package pack2;
6: $var = 34;
7: package pack1;
8: print ("$var\n");
運行結果如下:
$ program
26
$
第三行定義了包pack1,第四行建立變數$var,存貯在包pack1的符號表中,第五行定義新包pack2,第六行建立另一個變數$var,存貯在包pack2的符號表中。這樣就有兩個獨立的$var,分別存貯在不同的包中。第七行又指定pack1為當前包,因為包pack1已經定義,這樣,所有變數和子程式的定義和調用都為該包的符號表中存貯的名字。因此第八行對$var的調用為pack1包中的$var,其值為26。
3、main包
存貯變數和子程式的名字的預設符號表是與名為main的包相關聯的。如果在程式裡定義了其它的包,當你想切換回去使用預設的符號表,可以重新指定main包:
package main;
這樣,接下來的程式就好象從沒定義過包一樣,變數和子程式的名字象通常那樣存貯。
4、包的引用
在一個包中可以引用其它包中的變數或子程式,方法是在變數名前面加上包名和一個單引號,如:
package mypack;
$var = 26;
package main;
print ("$mypack‘var\n");
這裡,$mypack‘var為mypack包中的變數$var。
注意:在Perl 5中,包名和變數名用雙冒號隔開,即$mypack::var。單引號引用的方式仍然支援,但將來的版本中未必支援。
5、指定無當前包
在Perl 5中,可以用如下語句指定無當前包:
package;
這時,所有的變數必須明確指出所屬包名,否則就無效--錯誤。
$mypack::var = 21; #ok
$var = 21; #error - no current package
這種情況直到用package語句指定當前包為止。
6、包和子程式
包的定義影響到程式中的所有語句,包括子程式,如:
package mypack;
subroutine mysub {
local ($myvar);
# stuff goes here
}
這裡,mysub和myvar都是包mypack的一部分。在包mypack外調用子程式mysub,則要指定包:$mypack‘mysub。
可以在子程式中切換包:
package pack1;
subroutine mysub {
$var1 = 1;
package pack2;
$var1 = 2;
}
這段代碼建立了兩個變數$var1,一個在包pack1中,一個在包pack2中,包中的局域變數只能在其定義的子程式等語句塊中使用,像普通的局域變數一樣。
7、用包定義私人資料
包最通常的用途是用在含有子程式和子程式所使用的全域變數的檔案中,為子程式定義這樣的包,可以保證子程式使用的全域變數不可在其它地方使用,這樣的資料即為私人資料。更進一步,可以保證包名不可在其它地方使用。私人資料例:
1 : package privpack;
2 : $valtoprint = 46;
3 :
4 : package main;
5 : # This function is the link to the outside world.
6 : sub printval {
7 : &privpack‘printval();
8 : }
9 :
10: package privpack;
11: sub printval {
12: print ("$valtoprint\n");
13: }
14:
15: package main;
16: 1; # return value for require
此子程式只有在調用printval後才能產生輸出。
該檔案分為兩個部分:與外界聯絡的部分和私人部分。前者為預設的main包,後者為包privpack。第6~8行定義的子程式printval可被其它程式或子程式調用。printval輸出變數$valtoprint的值,此變數僅在包privpack中定義和使用。第15、16行確保其被其它程式用require語句包含後工作正常,15行將當前包設定回預設包main,16行返回非零值使require不報錯。
8、包和系統變數
下列變數即使從其它包中調用,也在main包中起作用:
- 檔案變數STDIN, STDOUT, STDERR 和 ARGV
- 變數%ENV, %INC, @INC, $ARGV 和 @ARGV
- 其它含有特殊字元的系統變數
9、訪問符號表
在程式中尋找符號表可用數組%_package,此處package為想訪問的符號表所屬的包名。例如%_main含有預設的符號表。
通常不需要親自尋找符號表。
三、模組
多數大型程式都分割成多個組件,每一組件通常含有一個或多個子程式及相關的變數,執行特定的一個或多個任務。集合了變數和子程式的組件稱為程式模組。
1、建立模組
Perl 5中用包來建立模組,方法是建立包並將之存在同名的檔案中。例如,名為Mymodult的包存貯在檔案Mymodult.pm中(副檔名.pm表示Perl Module)。下例的模組Mymodult含有子程式myfunc1和myfunc2及變數$myvar1和$myvar2。
1 : #!/usr/local/bin/perl
2 :
3 : package Mymodule;
4 : require Exporter;
5 : @ISA = qw(Exporter);
6 : @EXPORT = qw(myfunc1 myfunc2);
7 : @EXPORT_OK = qw($myvar1 $myvar2);
8 :
9 : sub myfunc1 {
10: $myvar1 += 1;
11: }
12:
13: sub myfunc2 {
14: $myvar2 += 2;
15: }
第3~7行是標準的Perl模組定義方式。第3行定義包,第4行包含內建Perl模組Exporter,6、7行進行子程式和變數的輸出以與外界聯絡。第6行建立名為@EXPORT的特殊數組,該數組中的子程式可以被其它程式調用,這裡,myfunc1和myfunc2可以被訪問。其它任何在模組中定義但沒有賦給數組@EXPORT的子程式都是私人的,只能在模組內部調用。第7行建立另一個名為@EXPORT_OK的特殊數組,其中含有可被外部程式訪問的變數,這裡含有$myvar1和$myvar2。
2、匯入模組
將模組匯入你的Perl程式中使用use語句,如下句匯入了Mymodule模組:
use Mymodule;
這樣,模組Mymodule中的子程式和變數就可以使用了。
取消匯入模組使用no語句,如下句取消了Mymodule模組的匯入:
no Mymodule;
下面看一個匯入模組和取消匯入的例子,使用integer模組要求所有數字運算基於整數,浮點數在運算前均被轉化為整數。
1: #!/usr/local/bin/perl
2:
3: use integer;
4: $result = 2.4 + 2.4;
5: print ("$result\n");
6:
7: no integer;
8: $result = 2.4 + 2.4;
9: print ("$result\n");
程式輸出如下:
$ program
4
4.8
$
如果use或no語句出現在語句塊中,則只在該塊的有效範圍內起作用,如:
use integer;
$result1 = 2.4 + 2.4;
if ($result1 == 4) {
no integer;
$result2 = 3.4 + 3.4;
}
$result3 = 4.4 + 4.4;
結果輸出如下:
4
6.8
8
這裡,no語句只在if語句中有效,出了if語句仍使用integer模組,因此4.4在做加法前被轉化成了4。
3、預定義模組
Perl 5提供了許多有用的預定義模組,可以用use匯入和no語句取消。下面是庫中最有用的一些模組:
integer |
使用整數運算 |
Diagnostics |
輸出較多的診斷資訊(警告) |
English |
允許英文名用作系統變數的別名 |
Env |
匯入環境變數的Perl模組 |
POSIX |
POSIX標準(IEEE 1003.1)的Perl介面 |
Socket |
裝載C語言的通訊端處理機制 |
Perl文檔中有完整的預定義模組列表。
註:世界各地的Perl 5使用者寫了許多有用的模組,CPAN(Comprehensive Perl Archive Network)的Perl文檔有其完整的列表。關於CPAN的更多資訊見其網址:http://www.perl.com/perl/CPAN/README.html。
上一章 下一章 目錄
perl 第十四章 Perl5的包和模組