2008-05
29

哦……Perl真的是太博大精深了。尤其是它的One-Liner程序,每一行都是优美的杰作啊。

下面搜集了一些很有用的One-Liner。大部分资料来自于 这里这里这里

阅读全文 »

2008-03
27

今天继续翻译代码布局的这一部分。有些很明显的条目,就不翻译了,看看代码就应该明白是什么意思。

阅读全文 »


2008-03
18

今天在写代码时遇到一个很奇怪的问题。我写的这样一个函数:

sub parse {
  my $self = shift;
  my $header = shift;
  my $charset = shift;
  
  # 其它处理
  # ...
}

至于如何调用,则是先通过 MIME::Parser 模块解析邮件, 然后调用解析结果中的 MIME::Head::get 函数来获得邮件头的值, 再将其结果传递给 parse 函数:

print $self->parse($head->get("Cc"), "GB2312");

通常都没有问题,在parse函数中,$header取到 Cc 邮件头,而 $charset 取到 "GB2312"; 但当一封邮件中不存在Cc时,就会出现一件怪事: $header 变量竟然取到了第二个参数 "GB2312" 的值!

在 parse 中将 @_ 打出来,发现当Cc邮件头取不到时,第一个参数就“消失”了, 参数列表就变成了 ("GB2312")。按照常理,参数列表应该是 (undef, "GB2312")才对啊。

百思不得其解中,偶然看到 MIME::Head::get 的文档中这样说:

  • If a numeric INDEX is given, returns the occurence at that index, or undef if not present:
  • If no INDEX is given, but invoked in a scalar context, then INDEX simply defaults to 0:
  • If no INDEX is given, and invoked in an array context, then all occurences of the field are returned:

上面的调用属于没有 INDEX 的情况,莫非parse函数的参数列表提供了一个列表环境, 导致MIME::Head::get返回了一个数组?

于是强制为它提供一个标量环境,结果就正常地得到了 (undef, "GB2312")。

print $self->parse(scalar $head->get("Cc"), "GB2312");

看来果然是上下文环境造成的问题。parse函数的第一个参数期待一个常量, 而函数的参数列表却是一个列表环境,再加上MIME::Head::get在列表环境中返回数组, 这样就会导致两种错误:

  • 如果不存在Cc,则参数列表为("GB2312"),$header = "GB2312";
  • 如果存在多个Cc,则参数列表为("Cc-val1", "Cc-val2", "Cc-val3", "GB2312"), $charset="Cc-val2"。

两种情况都十分荒谬。

对Perl的上下文不熟悉的人,这里可要特别注意啊。



2008-03
11

继续翻译Perl最佳实践的2.9~2.12。

阅读全文 »


2008-03
08

停止已久的《Perl Best Practices》翻译今天重新开始了。继续讲代码布局。

阅读全文 »


2008-03
07

最近遇到的一个任务是要将散落在系统各个文件中的 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都会被加载。



2008-01
29

上周偶然与fcicq讨论到一个关于perlcc的优化问题。 据说用perlcc将perl程序编译成C程序后再用gcc -O3进行优化,速度可能会快一些。 于是就测了测,顺便试了试其他语言的情况。

测试程序是Ackermann函数。 也许用它来做benchmark不太合适,但毕竟这是个纯数学+多次递归+耗时的运算,也能反映一定问题吧。

阅读全文 »

2007-12
14

那天和人讨论一个简单的应用:集合减法,即给定集合 A 和 B ,计算 C = A - B, 也就是说求属于A但不属于B的元素。集合A和B使用数组来存储,元素仅限于整数。 求计算方法。

Perl自身没有集合运算函数,为了这一个功能也犯不上去安装CPAN模块, 所以我们考虑了以下几个方案:

  1. 利用foreach作最直白的循环来判断
  2. 利用grep代替foreach
  3. 将元素用逗号连接之后用正则表达式匹配
  4. 将数组转化成散列之后判断

四种方法都能实现,但效率如何?本以为使用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的散列算法不可小觑啊。

阅读全文 »

2007-11
30

最近在阅读《Perl Best Practices》(Perl最佳实践)这本书。 Damian Conway这这本大作从各个方面 阐述了Perl编程上的一些经验,是Perl编程人员的必读之物。 标题上的PBP即Perl Best Practices的缩写。 鉴于这本书目前还没有中译本,我准备将这本书摘录一些翻译过来, 给各位Perler作为参考。

阅读全文 »

2007-10
12

我习惯于将照片按照拍摄时间保存到硬盘上的 YYYYMMDD 目录下,但每次都手工查看拍摄时间再建目录、复制实在是费时费力,于是写了这个程序,放在桌面上只要双击一下就可以将照片按照日期复制过来。当然只考虑了Win32版,估计我不太可能在Linux下用相机……需要安装ActivePerl,并用ppm安装Win32::DriveInfo模块。

照片复制程序

另外,我只有一台T10,所以这个程序只考虑了T10的情况,各位读者不妨说说自己的相机的目录结构?我的相机目录结构如下:

型号:SONY Cybershot T10
根目录:DCIM
照片目录:101MSDCF, 102MSDCF, 103MSDCF, …, 999MSDCF