停止已久的《Perl Best Practices》翻译今天重新开始了。继续讲代码布局。
最近遇到的一个任务是要将散落在系统各个文件中的 use lib '/opt/mysoft/lib'; 这样的绝对路径引用改成相对路径, 以便整个系统能够轻易地移动到其他的目录中。系统是工作在Apache2 + mod_perl 下的,cgi程序位于 /opt/mysoft/cgi-bin 下。
初看起来似乎很容易:
use lib '../lib'; # 错误!
但实际尝试一下就会发现,这里的 '..' 似乎找错了位置。当前目录并不是cgi程序所在的 /opt/mysoft/cgi-bin。 经过一番实验后,发现在 use lib 时,当前目录居然是在 / 下!这样这条路就走不通了。
另一个想法是通过 $0 获取到CGI程序的路径,再根据此路径来找到 lib 目录不就可以了么?
$dir = dirname $0; use lib "$dir/../lib"; # 错误! print "$_\n" for @INC;
实际运行就会发现,这一招也行不通。原因是use lib是在编译时执行的。编译时代码并没有被执行, 因此 $dir 变量为空(甚至都不存在),于是就成了 use lib "/../lib",当然不正确了。 这样看来 use lib 似乎必须要书写完整路径了。
CPAN文档的解释如下:
lib - manipulate @INC at compile time
实际上 use lib 可以理解为 BEGIN { unshift @INC, '/opt/mysoft/lib'; }。 BEGIN段的内容在这一段编译结束后立即执行。
回到本来的问题上,如何能避免在系统中使用绝对路径? 其实使用mod_perl的功能一次性地指定好@INC,那么在CGI和pm模块中就无需再use lib了。
在mod_perl的官方文档中有关于如何调整@INC的说明。
一种方法是在 startup.pl 中书写完整路径:
use lib '/opt/mysoft/lib';
令一种方法是直接在httpd.conf中修改@INC:
PerlSwitches -I/opt/mysoft/lib
这样虽然不能完全避免绝对路径,但至少在产品代码中可以不再使用 use lib 了。
同样地,use 也是在编译时执行的。(参考)use Module;相当于
BEGIN { require Module; Module->import( LIST ); }
程序代码中的所有use指令都会在代码执行前被执行,因此use不论放在哪里,效果都是一样的。因此
if ($use_first_lib) {
use FirstLib;
...
}
else {
use SecondLib;
...
}
这样的代码是毫无用处的,不论$use_first_lib的值如何,FirstLib和SecondLib都会被加载。
上周偶然与fcicq讨论到一个关于perlcc的优化问题。 据说用perlcc将perl程序编译成C程序后再用gcc -O3进行优化,速度可能会快一些。 于是就测了测,顺便试了试其他语言的情况。
测试程序是Ackermann函数。 也许用它来做benchmark不太合适,但毕竟这是个纯数学+多次递归+耗时的运算,也能反映一定问题吧。
阅读全文 »那天和人讨论一个简单的应用:集合减法,即给定集合 A 和 B ,计算 C = A - B, 也就是说求属于A但不属于B的元素。集合A和B使用数组来存储,元素仅限于整数。 求计算方法。
Perl自身没有集合运算函数,为了这一个功能也犯不上去安装CPAN模块, 所以我们考虑了以下几个方案:
- 利用foreach作最直白的循环来判断
- 利用grep代替foreach
- 将元素用逗号连接之后用正则表达式匹配
- 将数组转化成散列之后判断
四种方法都能实现,但效率如何?本以为使用foreach应该是最快的, 没想到测试之后发现散列居然是最快的。
Rate use grep use regexp use foreach use hash use grep 359/s -- -58% -63% -90% use regexp 861/s 140% -- -11% -77% use foreach 965/s 169% 12% -- -74% use hash 3769/s 950% 338% 291% --
如果元素不是整数而是随机的字符串,正则表达式的效率会急剧下降,而散列依然能保证领先的地位。 看来Perl的散列算法不可小觑啊。
阅读全文 »最近在阅读《Perl Best Practices》(Perl最佳实践)这本书。 Damian Conway这这本大作从各个方面 阐述了Perl编程上的一些经验,是Perl编程人员的必读之物。 标题上的PBP即Perl Best Practices的缩写。 鉴于这本书目前还没有中译本,我准备将这本书摘录一些翻译过来, 给各位Perler作为参考。
阅读全文 »我习惯于将照片按照拍摄时间保存到硬盘上的 YYYYMMDD 目录下,但每次都手工查看拍摄时间再建目录、复制实在是费时费力,于是写了这个程序,放在桌面上只要双击一下就可以将照片按照日期复制过来。当然只考虑了Win32版,估计我不太可能在Linux下用相机……需要安装ActivePerl,并用ppm安装Win32::DriveInfo模块。
另外,我只有一台T10,所以这个程序只考虑了T10的情况,各位读者不妨说说自己的相机的目录结构?我的相机目录结构如下:
型号:SONY Cybershot T10
根目录:DCIM
照片目录:101MSDCF, 102MSDCF, 103MSDCF, …, 999MSDCF
Test::Base是什么?用官方的说法是“数据驱动的测试”。Test::Base是一个测试框架, 只要给它提供测试数据,它就能自动进行单元测试,省却了手工编写测试程序的麻烦。
可能有人用过Test::More模块进行自动测试,那么我推荐你使用Test::Base。Test::Base 与Test::More完全兼容,也就是说你可以仅仅将use Test::More;换成use Test::Base; 而不用改动任何其他代码;其次,Test::Base可以提供更为简单的测试方法, 让你不必在繁琐的测试程序上花费时间。
阅读全文 »众所周知Perl 5的面向对象并不是真正意义上的面向对象, 只是添加了一些函数使得它看起来像是面向对象而已。 通常bless出来的对象,它的属性可以被外部程序直接访问。 《Perl Hacks》的#43给出了一个方法,可以封装类属性, 使其不能直接被外部访问。另外 Class::Std 模块可以简单地实现这个方法。
阅读全文 »今天阅读《Perl Hacks》这本书,看到Ackermann函数和Memoize模块的介绍, 才想起以前在学习递归和算法的时候曾经见过这个Ackermann, 不过那时怎么也没有想到这个函数居然有如此强大的“威力”——发散得极其迅速, 以至于参数稍大一点就不可能算出它的精确值。于是它经常被用作电脑的benchmark……
Ackermann函数是数学史上和计算机史上的一个经典递归函数,它的表达式并不复杂, 但却几乎无法计算。其表达式如下:
A(m,n) = n+1 when m=0;
= A(m-1,1) when n=0;
= A(m-1,A(m,n-1)) otherwise.
用Perl语言写成代码如下:
sub ackermann {
my ( $m, $n ) = @_;
return $n + 1 if $m == 0;
return ackermann( $m - 1, 1 ) if $n == 0;
return ackermann( $m - 1, ackermann( $m, $n - 1 ) );
}
计算这个函数的值需要非常长的时间,因为该函数在 m 增加时发散得异常迅速…… 至于如何迅速大家可自行查资料(这个函数中文似乎叫做阿克曼函数)。
阅读全文 »书写代码时要保持良好的代码风格,缩进、注释、空行等东西一个都不能错。 不过这一点很难做到,即使是一个经验丰富的程序员也难免弄错, 更别说新手了。另外,有时阅读别人写得面条代码也是异常头疼。 不过 Perl 提供了 Perl::Tidy 这个工具可以直接将乱七八糟的 Perl代码整理成格式完美的代码。
我用的是 Windows 下的 ActivePerl 5.8, 因此首先要通过 ppm 安装 Perl::Tidy 软件包。安装方法很简单,启动 ppm 之后 s Perl-Tidy 然后 install 就可以了,不再细说。
Linux下的同学们可以直接去 CPAN 下载源代码进行编译。
安装完成后会生成一个 perltidy 命令。Windows版也有这个命令(位于 C:\Perl\bin\perltidy.bat), 当然前提是你将 C:\Perl\bin 加到了你的 PATH 中。
对某个Perl源代码只要运行下面的命令即可:
perltidy foo.pl
整理好格式的代码会保存为 foo.pl.tdy。
我常用的编辑器是 gvim,自然想到如何将这个功能集成到 gVim 中。 方法很简单,建立 perl.vim,内容如下:
" 调用perltidy整理源代码 nnoremap ,pt :%!perltidy<CR> vnoremap ,pt :!perltidy<CR>
然后将 perl.vim 放到 C:\Program Files\Vim\vimfiles\ftplugin 目录下即可。 以后打开perl源代码之后,直接用 ,pt 快捷键或者用 Shift-V 选择之后再用 ,pt 即可整理正在编辑的代码的格式。
2008-2-14更新
为perltidy加上 -t -nola 参数之后,即可使用TAB缩进而不是空格缩进。perl.vim如下:
" 调用perltidy整理源代码 nnoremap ,pt :%!perltidy -t -nola<CR> vnoremap ,pt :!perltidy -t -nola<CR>
