|
perlfaq7 - 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> 不是用来指定档案的形态,亦非此把手的名字。它只是
将
字串加引号或使用分号及逗号是否绝对必要/还是完全没必要?通常一个没有冠上形态符号的字
(bareword)是不需被纳入引号里的,但在大多数的情况下或许该这麽做 (在 这些是和这些一样的 ------------ --------------- $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);
我如何暂时滤掉警告讯息?
{ local $^W = 0; #暂时关掉警告讯息 $a = $b + $c; #我知道这些变数可能未定义 } 注意,像所有的标点符号变数一样,目前不能对 一个发展中的新
什麽是一个扩充 (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"); 要避免此问题,须加上括号或是用超低优先的 (unlink $file) || die "snafu"; unlink $file or die "snafu"; 这些“英文的”运算子 ( 另一个拥有出人意料的优先顺序者为指数。它甚至高於负号,这使得
我如何宣告/生成一个资料结构 (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。以下是个范例 (里面没有用到任何系统呼叫,因为 sub is_tainted { return ! eval { join('',@_), kill 0; 1; }; } 然而,此方法会触发 【译注:这里所提的 ``被污染'' (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了 闭包用起来就像是个 函数样板,其中保留了一些可以在稍後再填入的空格。
把上面这个例子和下面这个
sub make_adder { my $addpiece = shift; return sub { shift + $addpiece }; } $f1 = make_adder(20); $f2 = make_adder(555); 这样一来 闭包在比较实际的场合中也常用得到,譬如当你想把一些程式码传入一个函数时: my $line; timeout( 30, sub { $line = <STDIN> } ); 如果要执行的程式码当初是以字串的形式传入的话,即
何谓变数自杀而我又该如何防止它?变数自杀指的是
(暂时或是永久)地失去一个变数的值。造成这个现象的原因是做范围界定的 从前【在旧版 perl的时代】大家写程式的时候很容易因为这样而不小心把变数值给弄丢。但现在 perl提供了一些保护措施,因此犯这种错的机率要小多了。 my $f = "foo"; sub T { while ($i++ < 3) { my $f = $f; $f .= "bar"; print $f, "\n" } } T; print "Finally $f\n"; 有叁个 ``bar''加进去的
我如何传递/传回一个 {函数,档案把手,阵列,杂凑阵列,方法,和正规表现式}?除了正规表现式这个特例外,你需要以传参考值的方式传资料给这些物件。参看 Pass by Reference,里面有针对此问题的讨论,以及 perlref 里面有参考值的资讯。
我如何生成一个静态变数?就像与 Perl相关的其他事情一样,``条条大路通罗马'' (TMTOWTDI)。对其他语言来说叫做 ``静态变数'' (static variable)的东西,在 Perl里面可能是一个函数私有的变数(只有该函数自己看得到,且在不同的呼叫间保持定值),或是一个档案私有(file-private)变数(只有同一个档案中的函数才看得到)。 以下就是实作函数私有变数的程式: BEGIN { my $counter = 42; sub prev_counter { return --$counter } sub next_counter { return $counter++ } }
要宣告一个档案私有(file-private)变数,你仍然得使用 package Pax; my $started = scalar(localtime(time())); sub begun { return $started } 当用
动态与文字式(静态)范围界定 (scoping)有何不同? Local()和 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''这个值都印不出来。那是因为 总结来说,local()不会产生你想像中的私有、区域变数。它只是将一个暂时的值授予一个全域变数。如果你要的是私有的变数,那麽
参看 perlsub ,里面有更详尽的解说。
当同一个范围中有一个文字式变数时,我该如何去撷取同名的动态变数?你可以透过符号式参考值
(symbolic references),把 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,而是指在
所谓深连结与浅连结 (deep and shallow binding)间有何不同呢?在深连结中,匿名函数中所用到的文字式变数值是以该函数产生时所在的范围为准。在浅连结中,这些变数值是以函数被呼叫时所在的范围为准,如果在这个范围中恰巧有同名的变数,便使用这些当地变数的值。Perl总是使用文字式变数(就是以
为何 "local($foo) = |