模組


使用模組

Perl 4 以前, 一個程式庫只不過是一個普通的 Xyz.pl 檔案, 裡面定義了很多常用函數; 要使用程式庫就用 "require Xyz.pl" 把它載進來. Perl 5 開始, 大部分的 Xyz.pl 程式庫檔都改名為 Xyz.pm 檔, 每個程式庫叫做一個 perl module (模組), 而且加入了物件導向的功能; 使用者則是以 "use Xyz;" 來載入 Xyz.pm 這個模組. 雖說 perl5 的模組加上了 oop 的功能, 但大部分提供 oop 界面的模組也仍舊提供非 oop 的界面. 以下的程式範例當中, Curses, Data::Dumper, Getopt::Std, Time::Local 等等都是「非 oop」的用法; Math::BigFloat 及 Tk 則是「oop」的用法; DB_File 和 GDBM_File 則用到特殊的 tie 語法.

  1. wday: 某年某月某日是星期幾?
  2. cchess: 象棋.
  3. tktest: 使用 tk
  4. pi: 計算 pi = 3.1415926535...
  5. db_file: DB_File 與 GDBM_File 的使用範例.

請試試看用各種方式執行 pi:


        ./pi -v
        ./pi -p 200
        ./pi -p 200 -v 
        ./pi -v -p 200
        ./pi -v -p200
        ./pi -vp200
 

Q: 如果用 DB_File 寫入, 用 GDBM_File 讀出, 會有什麼後果?

與模組相關的手冊:

  1. perlmod(1) 解釋模組的基本概念
  2. perlmodlib(1) 簡介有那些內附的標準模組, 及自行撰寫模組的方法.
  3. 查詢某一模組手冊: 例如從 perlmodlib(1) 知道有 Math::BigFloat 這個模組, 或用 find 得知 (見下文) 你的系統內有 .../Math/BigFloat.pm 這個檔案, 則可下 perldoc Math::BigFloat 查詢此模組的手冊.

需要跨越不同檔案使用 (例如要輸出 export 讓其他模組使用) 的全域變數, 應該用 use vars 宣告, 例如 cchess 當中的 $pos; 其他變數用 my 宣告比較好. (我的 perl/tk 範例程式有很多需要改...) 注意 use vars qw(...) 括弧內的變數名稱之間不要有逗點.

每個模組定義了一個 namespace (可以把它想成是變數的姓氏), 所以不同的模組當中可以出現名稱相同但其實毫不相干的變數, 就像是兩個同名不同姓的人一樣. 注意: 變數前面的 $, @ 或 % 要放在 namespace 的名稱之前, 例如 $Data::Dumper::Terse 及 $Math::BigFloat::div_scale. 平時未使用其他模組時, 所宣告的所有變數都屬於 main 這個 namespace. 以 $abc 這個變數為例, 全名應為 $main::abc. 注意: 舊的語法當中, 用 ' 來表示 :: 所以在 " $owner's house" 這樣的字串當中會造成奇怪的問題.

perl 的物件導向程式設計

sorry, 不太想深入介紹. 因為 oop 是後來才加入 perl 的, 設計上不太美觀... python 不知道是否好些?

  1. C++ 的 class 相當於 perl 的 module, package.
  2. C++ 的 object 相當於 perl 的 blessed reference.
  3. constructor (物件產生時應該執行的程式) 通常叫做 new, 或與類別名稱相同; destructor (物件消失前應該執行的程式) 不需要手動呼叫.
  4. 重要語法: 模組名稱->new(...) 用來產生某類別 (模組) 的新物件. 例: $p = Math::BigFloat->new("3");
  5. 重要語法: 物件->函數(...) 用來呼叫類別 (模組) 專屬的函數, 作用在物件上. 例: $x = $p->bsqrt();
  6. 上例其實是 $x = & Math::BigFloat::bsqrt($p); 的簡寫, 即 「呼叫 Math::BigFloat 模組內的 bsqrt 函數, 讓它作用在 $p 物件身上」. 因為 $p 為 blessed reference, perl 知道它屬於 Math::BigFloat 類別, 所以 「箭頭語法」 當中並未類別, perl 照樣可以推斷出我們要呼叫的是 Math::BigFloat 的 (而不是全域的) bsqrt. 但還是建議使用 「箭頭語法」, 因為完整語法不但比較囉嗦, 而且失去 oop 的一些特異功能. (例如 M 繼承其他類別時 ...)
  7. 其實 perl 的歷史包袱那麼重, 竟然還能夠做到讓類別使用者感覺上像是在使用真正的物件導向語言一樣, 已經很不容易了. 當然類別的作者寫起程式來就沒有那麼自然了. 有關 perl 的物件導向, 詳見 perltoot(1), perlobj(1), perlbot(1).

幾個常用模組

  1. Getopt::Std -- 處理命令列選項
    1. getopts 的第一個參數指定可以接受的選項名稱. 後面有 : 的, 表示在命令列上, 這個選項後面還有參數; 沒有 : 的選項 (叫 boolean 選項) 則只有開關兩種值
    2. getopts 的第二個參數是一個 hash 的 reference, 用來接受使用者在命令列上真正提供的選項值.
    3. 使用者下命令時, 可以把所有的 boolean 選項串在一起.
    4. 請參考 perlmonth 的文章
  2. Curses -- 文字模式下的特殊效果與遊標控制.
  3. Math::BigFloat -- 無限精準度的浮點數運算
    1. 記得先設定 $Math::BigFloat::div_scale 以指定運算時的有效數字位數.
    2. 為保險起見, 這個 package 在計算時, 有效數字可能會越來越多. 但其實對一般的演算法而言並無助益. 可用 substr 只印最前面的幾位數字.
    3. 注意: 這個模組顯示了在 perl 內也可以做到 overload. 詳見 perldoc overload.
  4. DB_File 及 GDBM_File -- 讓記憶體內的一個 hash 與檔案系統內的一個檔案內容 "緊密結合, 綁 (tie) 在一起", 使 hash 的內容變化立即反應到檔案內. 儲存格式為二進位檔, 適合用於資料結構簡單 (可以單獨一個 hash 表達), 資料量大的場合.
    1. 用 tie 連結變數與檔案; 用 untie 解開連結.
    2. DB_File 的開檔選項請參考 open(2)
    3. GDBM_File 的使用方式請見 gdbm(3)
  5. Data::Dumper -- 將程式中的變數印成字串. 若印到檔案中, 可以用來記憶程式的狀態 (例如棋盤現況). 效率稍低; 但寫出來的檔案就是一個 perl 程式, 用 vi 等文字檔編輯器就可以查/改, 方便 debug. 最簡單的用法: print F Dumper($var); 適合用於資料結構複製 (如 hash 內有 array, array 內又有 hash ...), 資料量小的場合.
  6. 類似功能的模組還有 Storable
  7. CGI -- 撰寫 cgi 程式的好幫手. 需要寫上傳檔案等複雜的功能時, 尤其好用.
  8. Text::* -- 許多分析/處理文字檔的模組
  9. Term::ReadLine -- 讓你的交談式程式提供一個方便的命令列環境

其他有用模組的極短使用示範:

  1. 將簡體文字檔轉成 big5 碼: perl -MEncode -pe 'Encode::from_to($_,"gb2312", "big5")' 簡體文字檔
  2. 以下片段為 gb2312 編碼的文字, 再用 base64 方式編碼。 base64 常用於 e-mail, 主要是為了將二進位檔的每個 byte 的內容都降到 127 以下。
            uMrUuM7+yfy7+bG+19TTydLUu7vIodK7teO2zNTdsLLIq7XEyMssCrzIsrvF
            5M/tytywssirLCDSsrK7xeTP7crc19TTyaGjCgkJICAgICAgLS0gsOC93MP3
            ILilwLy/y8HWCgpUaGV5IHRoYXQgY2FuIGdpdmUgdXAgZXNzZW50aWFsIGxp
            YmVydHkgdG8Kb2J0YWluIGEgbGl0dGxlIHRlbXBvcmFyeSBzYWZldHkgZGVz
            ZXJ2ZQpuZWl0aGVyIHNhZmV0eSBub3IgbGliZXJ0eS4KCQkgICAgICAtLSBC
            ZW5qYW1pbiBGcmFua2xpbgoK
    
    將它存檔, 叫做 freedom.txt, 可以用下列指令解碼: perl -MMIME::Base64 -MEncode -e '$t=join("",<>); $t=decode_base64($t); Encode::from_to($t,"gb2312","big5"); print $t' freedom.txt 如果嫌指令太長, 可以考慮改用 ./base64 -d freedom.txt | iconv -f gb2312 -t big5; 不過我覺得學 perl 版的指令比較好, 因為不論在任何系統, 都不必另外安裝套件, 且還有許多其他編碼轉換模組。
  3. sitio 函式庫更方便的 ANSI escape sequence 介面: perl -MTerm::ANSIColor -e 'print colored("hello!\n", "blue on_yellow underline")'

管理模組

自由軟體的文化, 鼓勵軟體作者 站在巨人的肩膀上 -- 盡量拿現成的程式/模組來用。 甚至於很多看似與 perl 無關的應用軟體, 其實也用到 perl 模組。 所以不只是 perl 程式設計師需要學會使用 perl 模組, 單純的軟體使用者學一點點管理 perl 模組的常識, 也很有幫助。 這一節前半我們先不談程式設計, 純粹從軟體使用者的角度著眼: 當你安裝軟體時, 系統 (或軟體的文件) 告訴你必須先安裝某些 perl 模組, 應該怎麼辦? 以下說明假設你的系統採用 rpm 方式管理套件, 而且你已熟悉 rpm 套件管理。

首先檢查一下, 說不定你的系統裡面其實已經有這個模組, 只是套件管理軟體不知道而已。 例如 algotutor 的文件說它需要 Tk, 那麼你可以下 perl -MTk -e '' 檢查。 如果沒有任何訊息, 表示你的系統裡面已經有 Tk 模組。 有時候系統裡明明有這些模組, 但 rpm 還是堅持相依問題未解決 (用 alien 指令從 .deb 轉換過來的 .rpm 經常出現這樣的問題), 可以試試看用 rpm -U --nodeps ... 強迫安裝。

如果找不到, 上面那個 perl 指令會告訴你它試了那些路徑, 還是找不到你要的模組。 這時請拿出你的 Linux 光碟, 尋找 「已包成 rpm 套件」 的 perl 模組。 通常套件名稱很容易猜出來, 例如 Tk 模組的 rpm 套件, 就叫做 perl-Tk-*.rpm; HTML::Parser 模組的 rpm 套件, 就叫做 perl-HTML-Parser-*.rpm。 如果光碟上找不到, 還可以到 rpmfind 網站找。

如果你要的 perl 模組, 找不到包成 rpm 套件的版本, 只好自己編譯。 通常自己編譯 perl 模組的步驟很簡單: 到 CPAN 的模組列表 找出該模組的原始碼, 然後下三個指令: perl Makefile.PL; make; make install 就完成了。

回到 perl 程式設計, 當你要用 perl 寫大程式時, 記得先看看是否已有某些函式庫可用:

  1. 查手冊 perlmodlib(1) 看看有那些已跟著 perl 安裝, 不必另外自行安裝的標準模組;
  2. 用 find `perl -e 'print "@INC"'` -name '*.pm' -print 列印出你系統內所有的模組 (標準模組及已額外安裝的);
  3. 到 CPAN 的 01modules.index.html 看看網路上有那些模組可以下載 (也許不要下載原始碼版本, 而是到 rpmfind 找 rpm 版比較方便)
  4. 到處去找合用的 C 程式庫, 看看是否可以用 SWIG 把它轉換成 perl 可以用的模組.

某些模組需要使用到其他模組 (記得嗎? 「站在巨人的肩膀上」, -- 這是自由軟體的文化) 因此有時安裝模組進行到一半, 才會發覺需要放下手邊的安裝程序, 先上網抓下並安裝更基本的模組. 如果這個「更基本的模組」又用到其他模組, ... 那就很麻煩了. 這時如果改用 CPAN 模組來幫你管理模組就簡單多了.

[2012/4/27 更新] 首先以系統管理員的身份 login, 並試著執行 perl -MCPAN -e shell 以進入 cpan 的交談式 shell。 第一次進入 cpan 的交談式 shell 時, 它會問你是否要幫你盡量把能夠設定的都全部自動設定。 就偷懶囉... 按 ENTER 讓它自動設定一切。 最後它會告訴你: 還剩一個東西必須手動設定。 按照指示打 o conf init urllist, 選擇 「亞洲」 「臺灣」 然後填入 [1-4] (之類的) 把臺灣所有映射站臺加入你的設定清單。

  1. 先安裝 readline 才有快速鍵可用: install Term::ReadLine::Perlinstall Term::ReadLine::Gnu 都可以。 (詳見 CPAN::FirstTime
  2. h 或 ? 可取得基本的 help.
  3. i /Tk/ 搜尋名為 Tk 的套件。 可以用 regexp。
  4. 設定檔在 /usr/lib/perl5/5.6.0/CPAN/Config.pm
  5. 回到 shell 下, 用 perldoc perllocal 可以查看 cpan 幫你安裝了那些模組.

CPAN::Mini 模組裡面有一支小程式 minicpan 可以幫你把 CPAN 精簡化抓回硬碟, 容量縮小到可以放進一張光碟。