那天和人讨论一个简单的应用:集合减法,即给定集合 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>
Perl的XML::DOM功能很强大,利用它可以轻易地分析XML文档,也可以建立XML文档。
常用的类包括:
- XML::DOM::Node:所有类的基类,表示一个XML节点。
- XML::DOM::Parser:XML解析器,将XML字符串解析成XML::DOM::Document对象。
- XML::DOM::Document:指向XML文档的根节点。
- XML::DOM::Element:指向一个元素,通常由XML::DOM::Document->createElement元素生成。
其他的类可以查看CPAN上关于 XML::DOM 的说明。
建立XML文档的通常做法是:首先通过 XML::DOM::Parser生成一个Document对象, 再通过这个Document对象的 createElement、createTextNode 等方法生成各个节点, 最后通过 XML::DOM::Node->appendChild 方法将节点添加到Document对象中, 最后再通过 toString 方法将 Document 转换为字符串输出。
下面是建立XML文档的一个例子:
#!/usr/bin/perl
use XML::DOM;
use XML::Twig;
# 创建XML的最外层元素
my $xml = "<MyXML/>";
# 通过XML::DOM::Parser解析外层元素,创建基础XML文档
# XML::DOM::Parser::parse函数的返回值为 XML::DOM::Document 类
my $parser = new XML::DOM::Parser;
my $dom = $parser->parse($xml);
# 生成Book节点
# createElement的返回值为 XML::DOM::Element 类
# XML::DOM::Element 的父类是 XML::DOM::Node 类
my $book = $dom->createElement("Book");
# 设置节点属性
$book->setAttribute("title", "My First Book");
$book->setAttribute("author", "charlee");
# 生成Chapter节点
my $chapter = $dom->createElement("Chapter");
$chapter->setAttribute("id", "1");
# 生成一个文本节点
my $title = $dom->createTextNode("My First Chapter");
$chapter->appendChild($title);
# 添加Chapter到Book
$book->appendChild($chapter);
# 添加Book到XML文档
$dom->getDocumentElement->appendChild($book);
# 利用XML::Twig整理格式
my $twig = new XML::Twig;
$twig->set_indent(" "x4);
$twig->parse($dom->toString);
$twig->set_pretty_print("indented");
# 输出
print $twig->sprint;
最后一段使用XML::Twig的程序只是为了整理XML输出结果的格式, 如果不需要整理格式,不调用XML::Twig,直接使用$dom->toString也可以。
总觉得在Perl里面读取文本文件应该用个循环:
while (<FH>) { print; }
或者是用数组:
@data = <FH>;
今天看代码时看到这样一段,真有种大开眼界的感觉。
$data = do { local $/; <FH>; };
查了查资料才明白:do表示执行后面括号内的语句,$/变量是读取文本文件时的行分隔符,默认是换行(\n)。 那么这段代码的意思是,声明 local $/,使得在 {} 内 $/ 的值变成空值,那么从<FH>读取时就不会再以 换行为分隔符,而是一次性将所有内容全部读出来。最后再用 do 将返回值赋给$data。
ActivePerl是Windows下最常用的Perl版本,它使用一个名为ppm的工具来管理模块。 安装Perl模块最简单的方法就是利用 ppm,search之后再install。但由于某些原因(比如ppm不能上网)导致不能直接 使用ppm安装模块时,可以利用下面的方法。
用浏览器打开ActiveState的Perl模块发布站, 然后选择相应的版本(一般是选择8xx的Windows目录)。注意模块列表页面比较大,须耐心等待。 模块列表打开后选择自己需要的zip包下载,例如我下载了 DBD-Oracle-1.17.zip。
下载之后将其解压到C:\下(不一定是C盘,但一定要解压到根目录下),出现两个文件:DBD-Oracle-1.17.tar.gz和 DBD-Oracle-1.17.ppd。然后打开命令行,切换路径到到 C:\,然后输入 ppm-shell 启动 ppm,输入以下命令即可安装。
install C:\DBD-Oracle-1.17.ppd



