歡迎您光臨本站 註冊首頁

perl常問問題集--第七篇

←手機掃碼閱讀     火星人 @ 2014-03-12 , reply:0
  譯者:陳彥銘

目錄
篇名
概述
我能拿到 Perl的 BNF/yacc/RE嗎?
$@%*這些符號是什麽意思?我怎麽知道何時該使用他們呢?
字串加引號或使用分號及逗號是否絕對必要/還是完全沒必要?
我如何跳過一些傳回值?
我如何暫時濾掉警告訊息?
什麽是一個擴充 (extension)?
為何 Perl運運算元的優先順序和 C的不一樣?
我如何宣告/生成一個資料結構 (structure)?
我如何創造出一個模組 (module)?
我如何創造一個類別 (class)?
我如何辨別一個變數是否被污染了(tainted)?
閉包 (closure)是啥?
何謂變數自殺而我又該如何防止它?
我如何傳遞/傳回一個 {函數,檔案把手,陣列,雜湊陣列,方法,和正規表現式}?
我如何生成一個靜態變數?
動態與文字式(靜態)範圍界定 (scoping)有何不同? Local()和 my()呢?
當同一個範圍中有一個文字式變數時,我該如何去擷取同名的動態變數?
所謂深連結與淺連結 (deep and shallow binding)間有何不同呢?
為何 "local($foo) = ;"無法正確地作用?
我如何重新定義一個內建函數、運運算元或是方法?
用 &foo和 foo()的方式來呼叫一個函數有什麽不同?
我如何作一個 switch或 case敘述?
我如何抓到呼叫未定義變數/函數/方法的事件?
為什麽我的程式會找不到放在同一檔案中的方法 (method)呢?
我如何找出目前所在的 package為何?
我如何將一大塊 perl程式碼變成註解?
作者與版權事宜

--------------------------------------------------------------------------------

篇名
perlfaq7 - Perl語言相關問題 ($(原文版 Revision: 1.18, Date: 1997/04/24 22:44:14 中譯版 $Revision: 1.1 $ $Date: 1998/01/16 23:57:56 $)


--------------------------------------------------------------------------------

概述
本篇內的問題主要是不適合納入其他篇章的一般性 Perl語言問題。


--------------------------------------------------------------------------------

我能拿到 Perl的 BNF/yacc/RE嗎?
不行,引用 Chaim Frenkel的話:「Perl的語法無法被簡化到可以用 BNF 表示。解析Perl的工作是分散於 yacc、lexer、煙霧和鏡子之間。」


--------------------------------------------------------------------------------

$@%*這些符號是什麽意思?我怎麽知道何時該使用他們呢?
他們都是指定形態 (type)用的符號,如同 perldata里所詳述的:

$純量值 (scalar) (數字,字串或參考值 [reference])
@陣列
%雜湊陣列 (關連陣列)
*代表同一個變數名的所有類形。在第四版中它們常用來達到指標
(pointers)的功能,但現在在新版的 perl中這個角色已被參
考值 (reference)取代了。

雖然這些符號在某些場合下可省略,但建議你隨處都用。

有些其他的符號你可能會碰到但卻不是指定形態用的有:

<>這是用來從一個檔案把手 (filehandle)里輸入一份記錄
\取某樣東西的參考值 (reference)

注意 < FILE> 不是用來指定檔案的形態,亦非此把手的名字。它只是 將<>這個運運算元用在 FILE這個把手上。在純量的情境 (scalar context) 下,它自 FILE 把手一次讀入一行 (嗯,該說一筆記錄,參看 $/),在序列情境 (list context)下,則一次將 全部的內容讀 入。當對檔案使用開、關或其它 <>之外的動作、或甚至只是提到把 手時,切記不要使用 <>。下面的用法是正確的:eof(FH) , seek(FH, 0,2) 以及 ``copying from STDIN to FILE''。


--------------------------------------------------------------------------------

字串加引號或使用分號及逗號是否絕對必要/還是完全沒必要?
通常一個沒有冠上形態符號的字 (bareword)是不需被納入引號里的,但在大多數 的情況下或許該這麽做 (在use strict下則是必須的)。但由一個簡單的字(不 能是一個已定義的副函數之名稱)所構成的索引值,和 =>左端的運運算元,都會被視為已納入引號了:

這些是和這些一樣的
------------ ---------------
$foo{line} $foo{"line"}
bar => stuff "bar" => stuff

一個區塊末端的分號可有可無,一個序列的最後一個逗號亦同。良好的寫作風格 (參看perlstyle)中建議除了在單行程式 (one-liners)的情況外都將他們加上去:

if ($whoops) { exit 1 }
@nums = (1, 2, 3);

if ($whoops) {
exit 1;
}
@lines = (
"There Beren came from mountains cold",
"And lost he wandered under leaves",
);


--------------------------------------------------------------------------------

我如何跳過一些傳回值?
一種方法是將傳回值當作序列來對待,然後用索引來指名其中的某個位置:

$dir = (getpwnam($user))[7];

另一種方法就是在等號左端用 undef 作元素:

($dev, $ino, undef, undef, $uid, $gid) = stat($file);


--------------------------------------------------------------------------------

我如何暫時濾掉警告訊息?
$^W變數 (在 perlvar中有說明)控制一個區塊在執行期 (runtime)的警告訊息:

{
local $^W = 0; #暫時關掉警告訊息
$a = $b + $c; #我知道這些變數可能未定義
}

注意,像所有的標點符號變數一樣,目前不能對 $^W用 my,只能用 local()。

一個發展中的新 use warnings編譯器指揮模組 (pragma) 提供了更精細的控制。好奇寶寶們應該翻翻 perl5-porters 郵件論壇的檔案庫以獲得更詳細的說明。


--------------------------------------------------------------------------------

什麽是一個擴充 (extension)?
一種從 Perl呼叫編譯好的 C程式碼的方法。閱讀 perlxstut是個多了解擴充(extensions)的好方法。


--------------------------------------------------------------------------------

為何 Perl運運算元的優先順序和 C的不一樣?
事實上它們是相同的。所有 Perl自 C借過來的運運算元都具備與原來在 C 中相同的優先順序。問題出在那些 C沒有的運運算元,特別是那些將其右方一律當成序列情境對待的函數,例如 print, chmod, exec等等。這類的函數被稱作「序列運運算元」(list operators),在 perlop的優先順序表中就是這麽稱呼。

一個常犯的錯誤像是:

unlink $file || die "snafu";

這會被解譯器看成是:

unlink ($file || die "snafu");

要避免此問題,須加上括弧或是用超低優先的 or運運算元:

(unlink $file) || die "snafu";
unlink $file or die "snafu";

這些「英文的」運運算元 (and, or, xor,及 not)是刻意設計成較一般序列運運算元低的優先順序,這就是為了解決前述的狀況。

另一個擁有出人意料的優先順序者為指數。它甚至高於負號,這使得 -2**2變成負四而非正四。他同時也會「向右靠」(right-associate),意思是說 2**3**2 代表二的九次方,而不是八的平方。


--------------------------------------------------------------------------------

我如何宣告/生成一個資料結構 (structure)?
一般來說,我們不 ``宣告''一個結構。用一個 (通常是匿名的) 雜湊陣列的參考值 (hash reference)即可。參看 perlref 以及 perldsc,裡面有更多資料。以下是一個範例:

$person = {}; #新的不具名雜湊陣列
$person->{AGE} = 24; #把 AGE欄的值設成 24
$person->{NAME} = "Nat"; #將 NAME欄設成 "Nat"

如果你要的是更嚴謹的寫法,看看 perltoot 。


--------------------------------------------------------------------------------

我如何創造出一個模組 (module)?
一個模組就是一個放在同名檔案里的包裹(package)。例如,Hello::There模組會 放在Hello/There.pm。perlmod 里有詳盡說明。Exporter 也會很有幫助。如果你正在寫一個 C 或是混合了 C及 Perl 的模組,那麽你就該讀 perlxstut 。

下面是個方便的樣板,你也許希望在撰寫第一個模組時將他派上用場。記得要改名 字。

package Some::Module; #假設是 Some/Module.pm

use strict;

BEGIN {
use Exporter ();
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);

##設定版本以備版本檢查之用;去掉 "#"號即可使用。
## $VERSION = 1.00;

#如果有使用 RCS/CVS,那應該考慮將下一行保留,
#但是小心兩位數版本編號可能造成的影響。
$VERSION = do{my@r=q$Revision: 1.1 $=~/\d+/g;sprintf '%d.'.'%02d'x$#r,@r};

@ISA = qw(Exporter);
@EXPORT = qw(&func1 &func2 &func3);
%EXPORT_TAGS = ( ); #例如: TAG => [ qw!name1 name2! ],

#整個包裹要輸出的全域變數(exported package globals)在此,
#還有其他選擇要輸出的函數。
@EXPORT_OK = qw($Var1 %Hashit);
}
use vars @EXPORT_OK;

#沒有輸出的全域變數在此。
use vars qw( @more $stuff );

#起始包裹內的全域變數,首先是要輸出的那幾個。
$Var1 = '';
%Hashit = ();

#接下來是其他的 (還是能以 $Some::Module::stuff的方式擷取他們的值)
$stuff = '';
@more = ();

#所有以檔案為範圍的變數名都
#必須在讓後面的函數使用前先創造出來。

#以檔案為範圍的變數名在此。
my $priv_var = '';
my %secret_hash = ();

#下面是一個以檔案為限的函數,當作一個閉包
#可以用 &$priv_func的方式呼叫它;但不能使用原型定義
my $priv_func = sub {
#程式碼放在這裡
};

#不論是否要輸出,都記得要將你的函數造出來。
#別忘了在 {}括弧間放些有趣的內容。

sub func1 {} #沒有定義原型
sub func2() {} #定原型為 void
sub func3($$) {} #定原型為兩個純量值

#這個函數雖然未被輸出,但是可以被呼叫
sub func4(\%) {} #定原型為一個雜湊陣列的參考值

END { } #模組清潔大隊在此 (global destructor)

1; #模組必須傳回真值


--------------------------------------------------------------------------------

我如何創造一個類別 (class)?
perltoot 裡面有對於類別和物件的介紹, perlobj 和 perlbot 也有。


--------------------------------------------------------------------------------

我如何辨別一個變數是否被污染了(tainted)?
參考 Laundering and Detecting Tainted Data。以下是個範例 (裡面沒有用到任何系統呼叫,因為 kill() 沒有將任何程序交給訊號):

sub is_tainted {
return ! eval { join('',@_), kill 0; 1; };
}

然而,此方法會觸發 -w參數的警告訊息。目前並無任何不會觸發 -w的方法可下偵測變數污染 -- 就把警告訊息當成是在提醒你,該把所有可能被污染的資料給 ``漂白'' (untaint)。

【譯註:這裡所提的 ``被污染'' (tainted),指的是當使用 -T這個參數時,或當 perl程式做 setuid或 setgid模式執行時 (在 UNIX 下), perl會自動將有安 全顧慮的變數列為受污染, 也就是 tainted。除非先做解除污染 (untaint)處理,否則 perl不會容許受污染的變數做出可能危害系統的舉動。因此 CGI程式應儘可能地使用 -T這個參數以策安全。】


--------------------------------------------------------------------------------

閉包 (closure)是啥?
關於閉包的說明,請看 perlref 。

閉包 (closure)是個精確但又很難解釋的電腦名詞。在 Perl 裡面,閉包是以 匿名函數的形式來實現,具有持續參照位於該函數範圍之外的文字式變數值的能力。 這些外部的文字變數會神奇地保留它們在閉包函數最初定義時的值 (深連結)。

如果一個程式語言容許函數遞迴另一個函數的話 (像 Perl 就是),閉包便具有意 義。要注意的是,有些語言雖提供匿名函數的功能,但卻無法正確處理閉包; Python 這個語言便是一例。如果要想多了解閉包的話,建議你去找本功能性程式 設計的教科書來看。Scheme這個語言不僅支援閉包,更鼓勵多加使用。

以下是個典型的產生函數的函數:

sub add_function_generator {
return sub { shift + shift };
}

$add_sub = add_function_generator();
$sum = &$add_sub(4,5); # $sum現在是 9了

閉包用起來就像是個 函數樣板,其中保留了一些可以在稍後再填入的空格。 add_function_generator() 所遞迴的匿名函數在技術上來講並不能算是一個閉包, 因為它沒有用到任何位在這個函數範圍之外的文字變數。

把上面這個例子和下面這個 make_adder()函數對照一下,下面這個函數所遞迴的匿名函數中使用了一個外部的文字變數。這種指名外部函數的作法需要由 Perl遞迴一個適當的閉包,因此那個文字變數在匿名函數產生之時的值便永久地被鎖進閉 包里。

sub make_adder {
my $addpiece = shift;
return sub { shift + $addpiece };
}

$f1 = make_adder(20);
$f2 = make_adder(555);

這樣一來 &$f1($n) 永遠會是 20加上你傳進去的值 $n ,而 &$f2($n) 將 永遠會是 555加上你傳進去的值 $n。$addpiece的值會在閉包中保留下來。

閉包在比較實際的場合中也常用得到,譬如當你想把一些程式碼傳入一個函數時:

my $line;
timeout( 30, sub { $line = } );

如果要執行的程式碼當初是以字串的形式傳入的話,即 '$line = ' ,那麽 timeout() 這個假想的函數在回到該函數被呼叫時所在的範圍後便無法再擷取 $list這個文字變數的值了。


--------------------------------------------------------------------------------

何謂變數自殺而我又該如何防止它?
變數自殺指的是 (暫時或是永久)地失去一個變數的值。造成這個現象的原因是做範圍界定的 my() 和 local()和閉包或 foreach()迴圈變數及函數參數相互影響 所致。

從前【在舊版 perl的時代】大家寫程式的時候很容易因為這樣而不小心把變數值 給弄丟。但現在 perl提供了一些保護措施,因此犯這種錯的機率要小多了。

my $f = "foo";
sub T {
while ($i++ < 3) { my $f = $f; $f .= "bar"; print $f, "\n" }
}
T;
print "Finally $f\n";

有叄個 ``bar''加進去的 $f 變數應該是一個新的 $f (因為 my $f在每個迴圈都應該創造一個新的區域變數)。然而,實際上並非如此。這個臭蟲在未來的 perl 版本中將會被修正。


--------------------------------------------------------------------------------

我如何傳遞/傳回一個 {函數,檔案把手,陣列,雜湊陣列,方法,和正規表現式}?
除了正規表現式這個特例外,你需要以傳參考值的方式傳資料給這些物件。參看 Pass by Reference,裡面有針對此問題的討論,以及 perlref 裡面有參考值的資訊。

傳遞變數和函數
一般的變數和函數是相當簡單的:只要傳一個指向現存的匿名變數或函數的參考值 即可:
func( \$some_scalar );

func( \$some_array );
func( [ 1 .. 10 ] );

func( \%some_hash );
func( { this => 10, that => 20 } );

func( \&some_func );
func( sub { $_[0] ** $_[1] } );

傳遞檔案把手
要創造出可以傳遞給函數使用的檔案把手,你可以用 *FH或 \*FH (這叫 ``typeglobs'' --請參看 perldata ),或是使用舊名 FileHandle的 IO::File模組以動態方式來產生檔案把手亦可,這兩個模組都附在標準 Perl 版本內。
use Fcntl;
use IO::File;
my $fh = new IO::File $filename, O_WRONLY|O_APPEND;
or die "Can't append to $filename: $!";
func($fh);

傳遞正規表示式
想要將正規表現式傳來傳去,你需要的將是使用 CPAN 里一個實驗性的正規表現式模組( Nick Ing-Simmons的 Regexp或 Ilya Zakharevich的Devel::Regexp),來傳遞字串,並且使用一個能捕捉例外情況的 eval敘述,或者你自己可以發明其他非常非常聰明的方式來做。以下就是一個如何以字串當作正規表現式,傳入一個做比較的函數的例子:
sub compare($$) {
my ($val1, $regexp) = @_;
my $retval = eval { $val =~ /$regexp/ };
die if $@;
return $retval;
}

$match = compare("old McDonald", q/d.*D/);

確定絕對不要用以下的寫法:

return eval "\$val =~ /$regexp/"; #錯誤

不然某人可以靠雙引號括起來的字串以及 eval 雙重解譯的本質而偷偷插入 shell指令來作壞事。例如:

$pattern_of_evil = 'danger ${ system("rm -rf * &") } danger';

eval "\$string =~ /$pattern_of_evil/";

想學非常非常聰明的方法的讀者可以參考 O'Reilly 出的 Mastering Regular Expressions這本書,作者是 Jeffrey Friedl。其中第 273頁的 Build_MatchMany_Function()特別的有趣。在 perlfaq2中可以找到有關本書 的資料。

如何傳遞方法 (methods)
你可以用下面的方法傳遞一個物件方法給一個函式:
call_a_lot(10, $some_obj, "methname")
sub call_a_lot {
my ($count, $widget, $trick) = @_;
for (my $i = 0; $i < $count; $i++) {
$widget->$trick();
}
}

不然你就用個閉包 (closure) 把物件和它的方法以及其參數都包在一起:

my $whatnot = sub { $some_obj->obfuscate(@args) };
func($whatnot);
sub func {
my $code = shift;
&$code();
}

你也可以研究 UNIVERSAL 類別中的 can()方法 (附於標準 Perl 版本中)。


--------------------------------------------------------------------------------

我如何生成一個靜態變數?
就像與 Perl相關的其他事情一樣,``條條大路通羅馬'' (TMTOWTDI)。對其他語言來說叫做 ``靜態變數'' (static variable)的東西,在 Perl裡面可能是一個函數私有的變數(只有該函數自己看得到,且在不同的呼叫間保持定值),或是一個檔案私有(file-private)變數(只有同一個檔案中的函數才看得到)。

以下就是實作函數私有變數的程式:

BEGIN {
my $counter = 42;
sub prev_counter { return --$counter }
sub next_counter { return $counter++ }
}

prev_counter() 和 next_counter() 將會共用一個於編譯時起始的私有變數 $counter。

要宣告一個檔案私有(file-private)變數,你仍然得使用 my(),將它放在檔案開頭處最外圍。假設現在是在 Pax.pm 這個檔案里:

package Pax;
my $started = scalar(localtime(time()));

sub begun { return $started }

當用 use Pax或 require Pax載入此模組時,這個變數就會被起始。不過它不會被資源回收,像其他出了有效範圍的變數那樣,因為 begun()函數要用到它,但是沒有其他函數能擷取它。這個變數不能以 $Pax::started 的形式來擷取,因為它所存在的範圍與此包裹無關。它存在的範圍是這個檔案。可想見地,一個檔案里可以放好幾個包裹,而所有的包裹都擷取同一個私有變數,但從另一個檔案中,即使是屬於同一個包裹(package),也不能取得它的值。


--------------------------------------------------------------------------------

動態與文字式(靜態)範圍界定 (scoping)有何不同? Local()和 my()呢?
local($x) 將全域變數 $x的原值存起來,並在此函數執行期間賦予一個新 值,此值可以從此函數所呼叫的其他函數里看見。這整個步驟是在執行期間完成的,所以才叫做動態範圍選取 (dynamic scoping)。local()影響的是全域變數,或者稱作包裹變數或動態變數。

my($x)會創造一個只能在目前這個函數里看得見的新變數。這個步驟是在編譯 期完成(compile-time),所以稱作文字式或是靜態範圍選取。my()總是作用在私 有變數,也稱作文字式變數或(不當地)稱作靜態(範圍選取)變數。

例如:

sub visible {
print "var has value $var\n";
}

sub dynamic {
local $var = 'local'; #授予 $var這個全域變數
visible(); #一個暫時的新值
}

sub lexical {
my $var = 'private'; #新的私有變數,$var
visible(); # (無法從此函數外看到)
}

$var = 'global';

visible(); #會印出 global
dynamic(); #會印出 local
lexical(); #會印出 global

你可以發現在整個過程中 ``private''這個值都印不出來。那是因為 $var的值只存在於lexical() 函數的區塊裡面,對它所呼叫的函數來說是看不到的。

總結來說,local()不會產生你想像中的私有、區域變數。它只是將一個暫時的值 授予一個全域變數。如果你要的是私有的變數,那麽 my() 才是你要找的。

參看 perlsub ,裡面有更詳盡的解說。


--------------------------------------------------------------------------------

當同一個範圍中有一個文字式變數時,我該如何去擷取同名的動態變數?
你可以透過符號式參考值 (symbolic references),把 use strict "refs"設定取掉。然後使用 ${'var'} ,而非 $var。

local $var = "global";
my $var = "lexical";

print "lexical is $var\n";

no strict 'refs';
print "global is ${'var'}\n";

如果你知道你所在的是哪一個包裹(package)的話,你可以直接指名,就像寫 $Some_Pack::var這樣。注意 $::var這個寫法 並非表示目前此包裹 (package) 內的動態變數 $var,而是指在 main包裹(package) 里的那個,就等價於 $main::var。直接指定包裹(package)的名稱雖然需要你把名字敲進程式碼 中,但是它執行起來比較快,也避免和 use strict "refs" 起衝突。


--------------------------------------------------------------------------------

所謂深連結與淺連結 (deep and shallow binding)間有何不同呢?
在深連結中,匿名函數中所用到的文字式變數值是以該函數產生時所在的範圍為準。在淺連結中,這些變數值是以函數被呼叫時所在的範圍為準,如果在這個範圍中恰巧有同名的變數,便使用這些當地變數的值。Perl總是使用文字式變數(就是以 my()創造的)式的深連結。然而,動態變數(也稱作全域(global),區域(local),或包裹(package)變數)在功效上是淺連結。就把這當作是少用它們的另一個理由好 了。請參考 閉包 (closure)是啥? 一節。


--------------------------------------------------------------------------------

為何 "local($foo) = ;"無法正確地作用?
local()會把 =號右邊以序列情境來對待。而 < FH>這個閱讀的 動作,就像 Perl里許多的函數以及運運算元一樣,會自動分辨出自己被呼叫時所在的情境並且採取適當的作法。一般來說,scalar()函數可以幫點忙。這個函數實際上對資料本身不會有任何作用(與一般所認為的相反),但是會告訴它所作用的函數要以對待純量值的方法來運算。如果那個函數沒有預先定義好碰到純量情境的行為,那麽它當然也幫不了你(例如 sort() 函數)。

然而,在以上這個例子 (local...)中,只要省略括弧便可強制使用純量情境:

local($foo) = ; #錯誤
local($foo) = scalar(); #可以
local $foo = ; #正確

其實在這個例子中,或許你該改用文字式變數 (lexical variables),不過會碰到 的問題跟上面一樣:

my($foo) = ; #錯誤
my $foo = ; #正確


--------------------------------------------------------------------------------

我如何重新定義一個內建函數、運運算元或是方法?
為什麽要這麽做? :-)

如果你要覆蓋掉某個內建函數,例如說 open(),那你得將其定義從另一個模組載 入。參考 Overriding Builtin Functions。在 Class/Template裡面也有個範例。

如果你要覆蓋掉一個 Perl運運算元,像是 +或 ** ,那你該使用 use overload這個編譯器指揮模組(pragma),其文件在 overload 。

如果你要覆蓋父類別 (parent class)里的方法呼叫 (method calls),請看 Overridden Methods 。


--------------------------------------------------------------------------------

用 &foo和 foo()的方式來呼叫一個函數有什麽不同?
當你用 &foo的方式呼叫一個函數時,你等於讓這個函數擷取你目前 @_裡面的值,同時也跳過原型定義 (prototypes)不用。這表式此函數抓到的是你當時的 @_, 而非一個空的 @_!雖然嚴格講起來它也不能算是個 bug (但是在 perlsub裡面是這麽說的)但在大部份情況下,這也算不上是個特別功能。

當你用 &foo()的方式呼叫你的函數時,你會得到一個新的 @_,但是原型定義 仍然會被避開不用。

在一般情況下,你該用 foo()的方式去呼叫函數。只有在編譯器已事先知道這 個函數的定義時,括弧才能省略,譬如當這個函數所在的模組或包裹被 use (但如果是被 require則不行)時,或是透過先前提及或 use subs宣告等 方法,讓編譯器先接觸到這個函數的定義。用這種呼叫方式,即使是當括弧省掉時, 你都會得到一個乾凈的 @_,不會有任何不該出現的舊值殘留在上面。


--------------------------------------------------------------------------------

我如何作一個 switch或 case敘述?
這個問題在 perlsyn 文件里有更詳盡的解釋。簡單來說,因為 Perl本身已提供了多種不同的條件測試方法可供使用 (數值比較、字串比較、 glob比較、正規表示式 對應、覆蓋比較,及其它),所以並沒有正式的 case敘述語法。雖然自 perl1起這就一直是許多人期盼的一個項目,但因 Larry無法決定怎樣才是呈現這功能的最好方法,因此還是將它略掉。

下面這個簡單的 switch範例以模式對應為基礎。我們將要做的是對儲存在 $whatchamacallit裡面的參考值 (reference)的類型進行多重條件的判斷。【譯註:$whatchamacallit 函意為 $what_you_might_call_it】

SWITCH:
for (ref $whatchamacallit) {

/^$/ && die '不是個參考值';

/SCALAR/ && do {
print_scalar($$ref);
last SWITCH;
};

/ARRAY/ && do {
print_array(@$ref);
last SWITCH;
};

/HASH/ && do {
print_hash(%$ref);
last SWITCH;
};

/CODE/ && do {
warn '無法印出函數的 ref';
last SWITCH;
};

# DEFAULT

warn '跳過使用者自定的類型';

}


--------------------------------------------------------------------------------

我如何抓到呼叫未定義變數/函數/方法的事件?
在 Autoloading 和 AUTOLOAD: Proxy Methods里 提到的AUTOLOAD 方法讓你能捕捉對於未定義函數與方法的呼叫。

如果是要處理一些在 -w之下觸發警告訊息的未定義變數,你可以使用一個處理元 (handler)來捕捉 __WARN__這個虛擬信號 (pseudo-signal),範例如下:

$SIG{__WARN__} = sub {

for ( $_[0] ) {

/Use of uninitialized value/ && do {
#將警訊提升為致命行動
die $_;
};

#其它要捕捉的狀況可以寫在此。

warn $_;
}

};


--------------------------------------------------------------------------------

為什麽我的程式會找不到放在同一檔案中的方法 (method)呢?
一些可能的原因:你用的繼承給搞混了、你拼錯了該方法的名字,或是物件的類別 錯誤。這些事在 perltoot里都有更詳盡的說明。同時你也可以用 print ref($object) 來找出 $object這個物件是被歸到哪個類別底下。

另一個可能的原因是你在 Perl還不知道這個包裹 (package)存在之前便將某個 類別名稱在間接式物件語法中使用 (例如 find Guru "Samy")。最好是在開始使用你的包裹前,先確定都已經先把它們定義好了,如果你用的是 use 而非 require的話,這件事便會自動處理好。不然的話,確定你使用箭頭式語法 (例如,Guru-find(``Samy'')>)。在perlobj 裡面對於物件的記號有詳盡解釋。


--------------------------------------------------------------------------------

我如何找出目前所在的 package為何?
如果只是一個隨意的程式的話,你可以用下面的方法找出目前正被編譯的包裹為何:

my $packname = ref bless [];

但如果是一個方法的話,而且印出的錯誤訊息中要包含呼叫此方法的物件 (不見得 就是把這個方法編譯進去的那個物件)則:

sub amethod {
my $self = shift;
my $class = ref($self) || $self;
warn "我是被 $class這個物件所召喚";
}


--------------------------------------------------------------------------------

我如何將一大塊 perl程式碼變成註解?
用內嵌 POD格式的方法把程式碼變註解:

#這是程式

=for nobody
這段就變成了註解

#程式繼續下去

=begin comment text

接下來此處所有

的文字都會被
所有人忽略

=end comment text

=cut


--------------------------------------------------------------------------------

作者與版權事宜
Copyright (c) 1997 Tom Christiansen and Nathan Torkington. All rights reserved.有關使用 、(轉)發行事宜 ,詳見 perlfaq

譯者:陳彥銘、蕭百齡

中譯版著作權所有:陳彥銘、蕭百齡及兩隻老虎工作室。 本中譯版遵守並使用與原文版相同的使用條款發行。


[火星人 ] perl常問問題集--第七篇已經有626次圍觀

http://coctec.com/docs/program/show-post-71996.html