perlfaq3 -程式设计工具(原文版 Revision: 1.22,
Date: 1997/04/24 22:43:42. 中译版 $Revision: 1.4 $, $Date: 1997/07/12 20:03:10 $)
这个部份回答了有关程式设计师的工具与程式设计方面的协助等相关问题。
你到 CPAN(见 perlfaq2)找过了吗?也许别人已经写了某个模组可以解决你的
问题。你查过相关的说明文件了吗 (man pages)?以下是一份概要的索引:
物件 (Objects) perlref, perlmod, perlobj, perltie
资料结构 (Data Structures) perlref, perllol, perldsc
模组 (Modules) perlmod, perlmodlib, perlsub
正规表示法 (Regexps) perlre, perlfunc, perlop
升级至 Perl5 (Moving to perl5) perltrap, perl
与 C连结 (Linking w/C) perlxstut, perlxs, perlcall, perlguts, perlembed
杂项 (Various) http://www.perl.com/CPAN/doc/FMTEYEWTK/index.htm
(不是说明文件,但还是很有用)
perltoc里有一份粗略的
perl 说明文件组的目录。
典型的作法是使用 perldebug(1)说明文件里提到的
Perl
除虫器,在一个「空的」(译者:即不存在的)程式上执行,像这样:
perl -de 42
接下来所打入的任意合法 Perl程式码皆会立刻被评估。同时,你可以检查符号表
(symbol table)、取得堆叠的记录 (stack backtraces)、检视变数值、设定阻断点
(set breakpoints) 以及其他符号式除虫器 (symbolic debuggers)
所能作的动作。
基本上来说,没有。Shell.pm模组 (是 perl
标准套件之一)只是叫 perl 将非 Perl语言的命令当作 shell的命令来试着执行看看罢了。perl原始码套件中的
perlsh,功能简易,也很无趣,不过仍可能是你所要的。
你用过 -w吗?
你试过 use strict吗?
你是否检查过每个系统呼叫 (system call)所传回的值?
读了 perltrap说明文件吗?
你试过 perldebug里所提到的
Perl除虫器吗?
你该自 CPAN抓取 Devel::DProf 模组,并且使用
perl 标准套件所附的 Benchmark.pm。 Benchmark.pm让你测量程式码的某部份在执行上所花的时间,而
Devel::DProf则详细地替你分析哪一部份的程式用掉多少时间。
随着新发行的 alpha版 Perl编译器(它不在一般标准套件里)而来的
B::Xref模组可 以替你的 Perl程式制作 cross-reference报告。用法是:
perl -MO=Xref[,OPTIONS] foo.pl
C有 indent(1)可以将原始码格式美化,但
Perl并没有能做得像它那麽好的东西。扫瞄器 (scanner) 和分析器 (parser)
间复杂的反馈 (feedback)(把 vgrind 和 emacs等程式搞混的就是这反馈)使得撰写一个独立的
Perl 分析器成了一项艰巨的挑战。
当然,若你直接照 perlstyle里面的指示写程式,就根本没有必要重新安排格式。
你所用的编辑器可以并也应能帮你把原始码的格式弄漂亮些。像
emacs的 perl-mode就能帮你把大部分 (但非全部)的程式码排列得漂亮些,而其它普通的编辑器也能提供一定程度的协助。
如果你试着使用 vgrind程式从雷射印表机印出漂亮的原始码,可以参考:
http://www.perl.com/CPAN/doc/misc/tips/working.vgrind.entry
,但是碰到复杂的程式码可能就不能全然令人满意了。
有个简单的在 http://www.perl.com/CPAN/authors/id/TOMC/scripts/ptags.gz
也许符合你的需要。
在 ftp://ftp.perl.com/pub/vi/toms.exrc
有完整的 Tom Christiansen之 vi设定档, 它是给 vi模拟器用的标准测试档
(standard benchmark file)。它与 nvi配合得最好,巧的是,这个出自 Berkeley的编辑器也可以内嵌一个
Perl直译器 --参看 http://www.perl.com/CPAN/src/misc
。
从大约 Emacs 19.22版 (version 19 patchlevel 22)起,已内含了
perl-mode.el及 perl 除虫器的支援。它们应该会和标准的 Emacs 19版一起出货。
在 perl原始码的目录下,你会找到一个叫作
``emacs'' 的目录,里面包括一个 cperl-mode
可以把程式中的关键字上色、提供内文相关的协助以及其它方便的功能。
注意:``main'foo''(其中的单引号)会让 emacs的
perl-mode生病,并且会弄乱内 缩 (indentation) 与精华 (hilighting)。不过你本来就该用
``main::foo''的 (译者按: main'foo 是表示模组或 package的旧式写法;新式的
[perl5的]写法是 main::foo)。
CPAN里的 Curses模组提供了一个通往 curses
程式库的动态载入物件模组介面。
Tk这个完全以 Perl
为基础,物件导向化的介面,让你不用学 Tcl也可以使用 Tk工具组。Sx则是
Athena Widget set专用的介面。两者都可在 CPAN取得。
http://www.perl.com/CPAN/authors/id/SKUNZ/perlmenu.v4.0.tar.gz
是个以 curses为基础的模组,可以达成你的要求。
若你的系统架构有支援的话,标准 perl
套件便应该有此功能(介由 DynaLoader 这个模组)。详情请参看 perlxstut 。
看下个问题。
最好是能设计一个较好的演算法 (algorithm),这通常会让程式有大不相同的表现。
骆驼书第八章里有些你或许想知道的增进效率小技巧。
其它方法包括自动载入较少使用的 Perl
程式码。请参看标准 perl 套件中的 AutoSplit及 AutoLoader模组的用法。或当你能断定程式执行效率的瓶颈在何处时,用
C来写那个部份,就像用组合语言来撰写 C程式的瓶颈部份一样。与此法相近的是使用以
C撰写瓶 颈部份的模组 (例如 CPAN中的 PDL 模组)。
在某些情况下,使用後端的编译器把程式编译成位元码
(byte code)(可节省编译时间) 或是将 perl程式转编译为 C程式的作法值得一试;这些作法绝对会节省编译的时间并且有时能省一些[但不多]执行时间
。请参考“编译你的 Perl程式”这个问题的答案。
如果你目前是将你的 perl直译器动态连结到
libc.so的话,重新作一份静态连结到 libc.a的 perl直译器可以提高 10-25%的执行效能。虽然这会使你的
perl直译器变得更胖,但你的 Perl程式 (及程式设计者)
或许会因此而感谢你。详情请参考 perl标准套件原始码版本中的 INSTALL
档案。
一些未经证实的报告中宣称有些使用 sfio的
Perl直译器表现得比没有用 sfio的还好 (针对於 IO频繁的应用程式)。想试试看?参考
perl套件原始程式版中的 INSTALL 档案,尤其是 ``Selecting File IO
mechanisms''这一段。
使用 undump程式把编译後的档案格式存到硬碟里以加快执行的速度已经是老掉牙的手法了。它已不再是个可行的方法,因为这方法只有几种平台能用,况且它终究不是个治本之
道。
当问题变成时间与空间的交易时, Perl
几乎总是用记忆体来帮忙解决问题。 Perl中的纯量 (Scalar)
耗掉的记忆体比 C中的字串形态还多,阵列又更多,
更别谈杂凑阵列了 (Hashes)。关於这一点,我们当然还有很多工作得作,近来发布的版本,已开始针对这些问题做改进了。例如,
5.004 版中, 重复的杂凑阵列索引值 (duplicate hash keys)
由使用它的杂凑阵列共用,这样就不用再重新定份位置给它了。
在某些情况下,使用 substr()或 vec()来模拟阵列有很大的好处。例如,一个有上千
个布林代数值的阵列将占用至少 20,000位元组的空间,但是它可以被转变为一个
125位元组的位元向量 (bit vector)以节省相当可观的记忆体。标准套件中的
Tie::SubstrHash模组也能够帮助特定形态的资料结构节省些记忆体。若你正在和一些特殊的资料结构奋战
(例如,矩阵),用 C写的模组所耗掉的记忆体可能低於同功能并用 Perl写的模组。
另一件值得一试的是,查一下你的 Perl是以系统内的
malloc 还是 Perl内含的 malloc
编译起来的。不论是哪个,试着换成另一个,再看看这是否造成任何差别。关於
malloc的资讯可在 perl标准套件原始码版中的 INSTALL
档案找到。键入 perl -V:usemymalloc就可以知道你是否在使用
perl的 malloc。
不,Perl的资源回收 (garbage collection)系统会解决此问题。
sub makeone {
my @a = ( 1 .. 10 );
return \@a;
}
for $i ( 1 .. 10 ) {
push @many, makeone();
}
print $many[4][5], "\n";
print "@many\n";
你无法这麽作。系统配置给程式的记忆体是覆水难收。这也是为何执行很长一段时间的
程式有时会重新执行 (re-exec)它们自己的原因。
然而,在使用你的变数时,明智地用 my()来定义执行范围,可让
Perl在脱离该范围後 将它们所占的空间释放给其它部份的程式。 (注:my()的变数也比全域变数执行起来快
10%。)当然,一个全域变数永远没有超出范围的时候,所以你无法将它占用的空间自动重新分配,不过,把它
undef() 或/和 delete()会有相同的效果。总之,在
Perl里,你并不能/应该去担心太多有关记忆体定址与解除这件事,而我们连添加这项功能(资料形态的预先定址),目前都已在进行中。
除了使一般 Perl程式加快或缩小的平常手段外,一个
CGI
程式还有其他的顾虑。也许它每秒会被执行好几次。每次它执行时,重新编译所花的时间、加上定址所需的
1 MB以上的系统记忆体,就是一个大杀手。光是编译成 C 是没啥帮助的
,因为瓶颈在於整个程序开始时所负担的包袱 (start-up overhead) 。
最起码有两种较流行的方法可以避免这些包袱。一种解法是将
mod_perl 或是 mod_fastcgi其中一个模组加在你所执行的 Apache HTTP server (可从
http://www.apache.org/ 取得)。有了 mod_perl 和
Apache::*模组 (从 CPAN取得),httpd执行时会带起一个内 嵌的 Perl直译器,而它会预先编译你的程式,并在不产生其它子程序的情况下用同一个定址空间来执行。Apache
扩充模组亦给 Perl一个连通 server API 的管道,所以用 Perl写的模组可以做到任何
C写的模组所具备的功能。而有了 FCGI模组 (自 CPAN取得),你以 sfio (参看
perl标准套件原始码版本中的 INSTALL档案) 和 mod_fastcgi (从 http://www.fastcgi.com/ 取得)模组编译成的 Perl
直译器将使你的每个 perl程式变成一个固定的 CGI 背景程序 (daemon
process)。
这些方法对你的系统与你撰写 CGI程式的方法都有超乎想像之外的影响,所以请小心地
探索它们。
删除它。 :-)
说真的,有一些具有不同“安全”等级的方法(大部分都不能令人满意)。
然而,首先,你 不能拿走读取权,不然你的程式怎麽被解译或是编译呢?
(不过那也并不表示一个 CGI程式的原始码可以被使用者读取。)所以你得让档案权限停留在
0755这个友善的阶段。
有些人认为这是个安全上的漏洞。不过若你的程式作的是不安全的事情,光仰赖别人
看不见这些漏洞、不知从何下手,那麽它依然是不安全的。其实对有些人来说他们并
不需要看见程式原始码便可能判定并揭露这些不安全的部份。透过隐瞒达到的安全,
就是不修正臭虫反而隐藏它们,实际上是没有安全性可言的。
你可以试着透过原始码过滤模组 (CPAN中的
Filter::*)来替原始码加密。但高手也许有
办法将其解密还原。你也可以用下面提到的 byte code
编译器与直译器,但高手也有可能反解译它。你可以试试後面提到的原生码编译器
(native-code compiler),但高手也有可
能反组译它。这些手段都需要不同难度的技巧才能让别人拿到你的原始码,但没有一种能
够很确定地隐藏它。(这对每种语言来说都为真,不是只有 Perl)
如果你所担心的是别人自你的程式码中获利,那麽一纸权限执照是能提供你法律上安全的唯一途径。注册你的软体并且写份权限说明,再加上一些具威胁性的句子像“这是
XYZ公司未出版的专有软体。你能撷取它并不代表你具有使用的权限...”之类云云。当然,我们不是律师,所以若你想要你的执照中每一句话在法庭上都站得住脚,就去见个律师吧。
Malcolm Beattie已经写了一个多功能的後端编译器,可以从
CPAN取得,它就能做到这两项功能。1997 年二月是 alpha测试版的最後几个阶段,这代表着若你是个程式设计
员而非寻找万灵解药的人,那麽参与其测试就会充满趣味。
请了解光是编译成 C
其本身或在本质上并不能保证它就会跑得快更多。那是因为除了
在运气好的状况中有一堆可以衍生成出来的原生形态外,平时的 Perl
执行系统环境依然
存在因此依然会花差不多长的执行时间与占用差不多大小的记忆空间。大多数程式能省下
来的不过是编译时间,这使执行速度顶多快 10-30%。有些罕见的程式能真正从中受利
(例如增快好几倍),但这还得配合原始码的微调。
Malcolm 将会主导 Perl 5.005
版的发展并试着将其编译器与多执行绪部份的工作融合进主要的发行版本里。
你或许会惊讶地发现,现行版本的编译器做出来的执行档大小跟你的
Perl直译器一样大,有时更大些。那是因为依照现在的写法,所有的程式皆转成一个被
eval()的大叙述。只要建造一个动态连结的 libperl.so程式库,并将之连结起来,你就可以戏剧性地减少这
种浪费。参看 perl原始码套件中的 INSTALL pod档案以获得更详尽的讯息。如果你用这方法连结你主要的
perl执行档,就能使它变得很渺小。举例来说,在作者之一的系
统里, /usr/bin/perl只有 11k“小”而已!
OS/2下只要用:
extproc perl -S -your_switches
当作 *.cmd档案的第一行 (-S
是因 cmd.exe中其 `extproc'处理的臭虫才要的)。DOS使用者应先制作一个相对的
batch 档案然後将它以 ALTERNATIVE_SHEBANG 的方式写成程式。(更多讯息在原始码版本的
INSTALL档案里)
若安装 Activeware版的 Win95/NT 专用 Perl,它会更动
Registry的内容,把 .pl 的扩充档名与 perl直译器结合。如果你安装另一版本或是用
WinGCC建构你自己的 Win95/NT用 Perl,那你就得自己更动 Registry的内容了。
麦金塔的 perl程式将会有适当的创造者与形态
(Creator and Type),所以双击它们就会执行这些 perl 应用程式。
重要:不论你做什麽,请千万不要因为觉得沮丧,就把
perl 直译器丢到你的 cgi-bin目录下,好让你的 web
伺服器能执行你的程式。这是一个非常大的安全漏洞。花点时间想
想怎样才是正确的做法吧。
可以。详情请看 perlrun。以下有些范例
(假设用的是标准的 Unix shell引言规则)。
#把第一栏和最後一栏相加
perl -lane 'print $F[0] + $F[-1]'
#辨别是否为文字档
perl -le 'for(@ARGV) {print if -f && -T _}' *
#移除 C程式中的说明
perl -0777 -pe 's{/\*.*?\*/}{}gs' foo.c
#让档案年轻一个月,躲避追杀的魔鬼 (daemon)
perl -e '$X=24*60*60; utime(time(),time() + 30 * $X,@ARGV)' *
#找出第一个未用的 uid
perl -le '$i++ while getpwuid($i); print $i'
#显示合理的使用说明路径 (manpath)
echo $PATH | perl -nl -072 -e '
s![^/+]*$!man!&&-d&&!$s{$_}++&&push@m,$_;END{print"@m"}'
好吧,最後一个例子事实上是「perl程式困惑化」竞赛
(Obfuscated Perl)的 参赛作品。 :-)
问题通常出在那些系统的命令解译器对於参数的引用与
Unix shells 所作的解释不同,而後者很不幸的是这些一行 perl
的生父。在某些系统,也许你得把单引号改成双引号,但这却是你万万
不可在 Unix或 Plan9系统上作的事。你也许还得把一个 %改成 %%。
例如说:
# Unix
perl -e 'print "Hello world\n"'
# DOS,等。
perl -e "print \"Hello world\n\""
# Mac
print "Hello world\n"
(然後执行 "Myscript"或按 Shift-Command-R)
# VMS
perl -e "print ""Hello world\n"""
问题是,这些方法没有一个是完全可靠的:它都得看命令解译器的脸色。在
Unix中,前两者通常可以用。在 DOS下,两者可能都没有用。若 4DOS是命令解译器,下面此法可能比
较有希望:
perl -e "print <Ctrl-x>"Hello world\n<Ctrl-x>""
在 Mac 下,端视你所用的环境为何。 MacPerl所附的
shell,或是 MPW, 其所支援的参数格式有不少都蛮像 Unix shells的,除了它自在地使用
Mac 的非 ASCII字元当成控制字元。
恐怕我得说这问题并没有一般解。白话一点说,它真是一团乱。
[部份答案是由 Kenneth Albanowski 所提供的。]
就模组来说,去 CPAN抓 CGI 和 LWP
两个模组。就书本来看,参考关於书那部份里特别和 web
相关的问题。若有与 web相关的疑难杂症,像“为何我收到 500错误”或“它在命令列模式下跑得好好的,怎麽不能在浏览器下正常执行”时,请参看:
The Idiot's Guide to Solving Perl/CGI Problems, by Tom Christiansen
http://www.perl.com/perl/faq/idiots-guide.htm
Frequently Asked Questions about CGI Programming, by Nick Kew
ftp://rtfm.mit.edu/pub/usenet/news.answers/www/cgi-faq
http://www3.pair.com/webthing/docs/cgi/faqs/cgifaq.shtml
Perl/CGI programming FAQ, by Shishir Gundavaram and Tom Christiansen
http://www.perl.com/perl/faq/perl-cgi-faq.htm
The WWW Security FAQ, by Lincoln Stein
http://www-genome.wi.mit.edu/WWW/faqs/www-security-faq.htm
World Wide Web FAQ, by Thomas Boutell
http://www.boutell.com/faq/
(译者:上面第叁份文件,Perl-CGI-FAQ的中译版可在
http://2Ti.com/cgi-bin/2T/perl/perl-cgi-faq-chi/
处取得。最後一份(WWW FAQ)的中译版可自 http://www.acer.net/document/cwwwfaq/
取得。)
perltoot 是个好开始,然後你可以再参考 perlobj 和 perlbot。Perltoot直到
5.004版本才诞生,但你可以从 http://www.perl.com/CPAN/doc/FMTEYEWTK/
下取得 (pod、html,或 postscript 格式)。
若你要从 Perl程式呼叫 C,就自 perlxstut开始向
perlxs ,xsubpp
,及 perlguts前进。反之,则读
perlembed ,perlcall ,及 perlguts
。别忘了
你可以从各模组的作者如何写他们的模组及解决他们的问题中学到很多。
自 CPAN 下载 ExtUtils::Embed 套件,然後执行
`make test'。如果测试成功,就一遍又一遍地读那些 pod
说明档案。若它失败了,参看 perlbug并送一份内有 make test
TEST_VERBOSE=1 与 perl -V输出的报告。
perldiag 有一份完整的 perl错误与警告讯息列表,并附有说明文字。你也可以用
splain程式 (伴随 perl而来)去解释这些错误讯息:
perl program 2>diag.out
splain [-v] [-p] diag.out
更改你的程式让它替你解释这些讯息也可以:
use diagnostics;
或
use diagnostics -verbose;
此模组 (亦为标准 perl 套件之一部份)设计的目的是要替一个模组从一
Makefile.PL 中自动撰写出一个 Makefile。详情请看 MakeMaker
。
Copyright (c) 1997 Tom Christiansen and Nathan Torkington.
All rights reserved.有关使用、(转)发行事宜,详见 perlfaq 。
译者:陈彦铭
中译版着作权所有:陈彦铭、萧百龄及两只老虎工作室。本中译版遵守并使用与
原文版相同的使用条款发行。
|