Perl 6 什么时候才能完成?

30 December 2004


原文地址:http://www.oreilly.com/pub/a/oreilly/ask_tim/2004/perl6_0804.html
背景介绍:这是一封由 Jag Venugopal 发给 perl.com 的Email。
作者简介:Simon Cozens ([email protected]), 发布过90几个perl modules, 他写的书包括《Beginning Perl》,《Extending and Embedding Perl》和即将出版的《Advanced Perl Programming》第二版。他还替O'Reilly维护perl.com网站。

译文

August/八月 2004
From: Jag Venugopal
Subject: When will Perl 6 ever get done? / Perl 6 什么时候才能完成?

Tim,

What's your take on when Perl 6 will be ready for general use? Its beginning to look like a project with no end and no production release.

对于何时才会有通用的 Perl 6,您有何看法?它开始像个永远不会结束和不会有产品发布的项目。

Jag


Hi Jag,

Your email was passed on to me, as perl.com editor. I've asked a number of Perl 6 designers and implementers, and gathered the following thoughts.

您的 email 由 perl.com 的编辑转发于我。我问过许多 Perl 6 的设计者和编写者,推断出如下想法。

The Perl 6 effort has come a long way since the July 2000 announcement that "the effort to write Perl 6 has begun in earnest," or the project roadmap that predicted "final design and language specification, October 2000," and "Beta release, July 2001." If we've learnt one thing since then, it's that making predictions about when Perl 6 will be released is very, very difficult.

从 2000 年七月通告“写 Perl 6 的工作已经如火如荼地开始了”,或曾经预测的项目里程碑“2000年十月发布最后的设计和语言规格说明”和“2001年七月发布测试版”,Perl 6 项目已经走了一段很长的路。如果说我们从(错误的预测)中学到了什么,那就是预测 Perl 6 何时发布是非常非常困难的。

There are many reasons for this. You said that Perl 6 is "beginning to look like a project with no end;" this is dead right--Perl 6 is just the next version of Perl, a project started over ten years ago and, we hope, with no end to its future. As Damian says, "Perl 5's first production release took the best part of a decade (counting from Perl 1), so if Perl 6 takes less than that I think we're doing okay."

对此我们有很多理由。您说 Perl 6 “开始像个永远不会结束的项目”,这没错——Perl 6 仅仅是 Perl 的下一版本,Perl 项目从十多年前开始的,我们希望,它永远不会结束。 正如 Damain 所说的:“Perl 5 的第一个发布版本用了一个十年(从Perl 1 开始算起)的大部分,所以如果 Perl 6 用的时间比它少,我认为我们做的将是成功的。”

As this implies, Perl 6 is something that we're working towards, and we have a pretty good starting point in Perl 5. We also have a good start in Parrot, the Perl 6 virtual machine, which will see a production release in October or November.

如此话所暗示的,我们正努力朝 Perl 6 前进,Perl 5 是个非常好的开始点。我们还有个很好的开始点 Parrot ,Perl 6 的虚拟机,我们可以在十月或十一月看到它的产品发布。

Because Perl 6 fits into the whole Perl development process, there's no hurry to get a 6.0.0 release out soon. And, indeed, when the developers are working completely on a voluntary basis, and with an industry slow to move to new technologies, there's no way to hurry up the process of development and acceptance anyway.

因为 Perl 6 要配合整个 Perl 开发进程,所以急急忙忙地发布 6.0.0 是没有必要的。而且事实上,由于开发者都是完全自愿参与和新技术是个被缓慢接受的过程,我们根本无法加快开发进程和接受度。

The plan is going to be to turn Perl 5 into Perl 6 steadily, over the course of many releases, just as happened with every release of Perl so far. Perl 5.10 is bringing in several Perl 6 features, and more will accrue as the Perl 5 series continues; at the same time, the Ponie Project (announced in July 2003) will move Perl 5 onto the Parrot VM.

我们的计划是通过许多个版本的发布将 Perl 5 平稳地过渡到 Perl 6,正如迄今为止我们都在做的每个 Perl 版本的发布。Perl 5.10 将引进一些 Perl 6 的特性,而且随着 Perl 5 系列的发展将会加入更多的特性。同时,Ponie 项目(于2003年七月通告)将允许 Perl 5 运行在 Parrot 虚拟机。

But after the previous set of predictions, very few people I talked to were willing to commit to giving a definite timeline for a Perl 6 release. Damian, for instance, told me that Perl 6 is essentially ready right now thanks to the Perl6::* modules on CPAN, and that "there is merely no production release yet;" in a recent interview, Allison Randal, the project manager, estimated a Perl 6 alpha at "around two years away."

但是在先前系列错误的预测之后,几乎没有人愿意再给出 Perl 6 发布的确切时间线(准确基线)。例如 DaMian 就说,“从 CPAN 上的 Perl6:: 模块来看,Perl 6 已经基本上准备好了”。对于“到目前为止没有任何产品发布”,在最近的采访中,项目经理 Allison Randal 估计两年左右会发布 Perl 6 alpha 。

If, however, having Perl 6 available faster is a pressing need for you, more developers helping out would always be a welcome way of bringing a release closer!

如果,无论如何,你很紧迫地需要 Perl 6,那么更多开发者来提供帮助是尽快发布的可行之道。

Hope this helps,
希望这些对您有所帮助,
Simon Cozens


使用h2xs打包模块

29 December 2004


描述

闲着无聊就看看了perlnewmod, 发现一种与“Module::Build”不一样的打包方式。
试了下,个人发现比Module::Build好一点。Module::Build打包出来的模块安装时必须要先安装Module::Build,而且h2xs还生成了简单的t(test)文件。而且我发现目前CPAN上的模块大多是用h2xs制作的。

h2xs 参数简介

    OPTION/选项:(不完整版,详细的请参考perldoc h2xs或h2xs -h)
  • -A, --omit-autoload 忽略autoload里的文件(-c里默认包含)
  • -B, --beta-version 使用alpha/beta型的版本号(如0.00_01),如果存在-v则忽略次参数
  • -C, --omit-changes 不创建CHANGES文件,而在POD模版中增加HISTORY段
  • -O, --overwrite-ok 允许覆盖已存在的扩展目录
  • -X, --omit-XS 忽略XS部分(-c和-f里默认包含)
  • -b, --compat-version 指定一个perl版本号用于向后兼容
  • -d, --debugging 显示调试信息
  • -n, --name 指定扩展的名字

过程

假设打包的是我的模块,Lingua::Han2PinYin
首先进入控制台(console)
>h2xs -AX -n Lingua::Han2PinYin
此命令的作用仅仅限于生成tar所需要的目录和一些文件。
lib/Lingua/Han2PinYin.pm
t/Lingua-Han2PinYin.t
Makefile.PL
README
Changes
MANIFEST
上面所生成的所有文件都是需要修改的。
  1. 将自己写的Han2PinYin.pm覆盖生成的Han2PinYin
  2. Makefile.PL里首先需要去掉"use 5.008004;"或者改为你所需要的版本号。其次要修改AUTHOR
  3. t下面的测试文件,如果需要增加新的测试则自行增加,不需要的话略过。
  4. README Changes 文件请自行修改
  5. MANIFEST则不需要修改
弄完后:
>cd Lingua-Han2PinYin
>perl Makefile.PL
>(n)make test
>(n)make dist
在Linux/Unix系统下用make,Win32系统下用nmake
至此差不多结束。会生成Lingua-Han2PinYin-0.04.tar.gz

Refer


快速开始Perl XML:接口篇

27 December 2004


原文URL: http://www.xml.com/pub/a/2001/04/18/perlxmlqstart1.html
by Kip Hampton
April 18, 2001

入门简介

最近在Perl-XML邮件组经常问起的问题是如何给不熟悉的用户一个对大量 Perl XML 模块的快速指引性概述文档。在接下来的几个月里我将单独对此问题写几篇专栏文章。

CPAN上的XML模块可以分成三大类:对 XML 数据提供独特的接口(通常有关在XML实例和Perl数据之间的转换),实现某一标准XML API的模块,和对一些特定的XML相关任务进行简化的特殊用途模块。这个月我们先关注第一个,XML Perl专用接口。

use Disclaimer qw(:standard);

此文档不是为了对模块性能进行基准测试,我的目的也不是暗示某一模块比另一个模块更有用。为你的项目选择正确的 XML 模块更多依赖于项目本身和你积累的经验。不同的接口适应于不同的任务和不同的人。我的唯一目的是通过定义两个简单的任务,然后提供不同借口的可运行例子来显示如何获得同样的最终结果。

任务

虽然XML的用途非常多,但大部分XML相关任务可分成两组:一是从已有的XML文档提取数据,另一个是使用其他资源的数据创建一个新的XML文档。既然如此,我们所用来介绍不同模块的例子将由“从一个XML文件中提取某一特定数据集”和“将一Perl数据结构转为某一特定XML格式”组成。

任务一:提取数据

首先,假设有如下XML片断:

<?xml version="1.0"?>
<camelids>
  <species name="Camelus dromedarius">
    <common-name>Dromedary, or Arabian Camel</common-name>
    <physical-characteristics>
      <mass>300 to 690 kg.</mass>
      <appearance>
        The dromedary camel is characterized by a long-curved 
        neck, deep-narrow chest, and a single hump.
        ...
      </appearance>
    </physical-characteristics>
    <natural-history>
       <food-habits>
         The dromedary camel is an herbivore.
         ...
       </food-habits>
       <reproduction>
         The dromedary camel has a lifespan of about 40-50 years
         ...
       </reproduction>
       <behavior>
         With the exception of rutting males, dromedaries show
         very little aggressive behavior.
         ...
       </behavior>
       <habitat>
         The camels prefer desert conditions characterized by a
         long dry season and a short rainy season.
         ...
       </habitat>
    </natural-history>
    <conservation status="no special status">
      <detail>
        Since the dromedary camel is domesticated, the camel has
        no special status in conservation.
      </detail>
    </conservation>
  </species>
  ...
</camelids>
现在我们假设此完整文档(可从本月例子代码中获取)包含骆驼家族所有成员的全部信息,而不仅仅是上面的单峰骆驼信息。为了举例说明每一模块是如何从此文件中提取某一数据子集,我们将写一个很简短的脚本来处理camelids.xml文档和在STDOUT上输出我们找到的每一种类的普通名(common-name),拉丁名(用括号包起来),和当前保存状况。因此,处理完整个文档,每一个脚本的输出应该为如下结果:
Bactrian Camel (Camelus bactrianus) endangered 
Dromedary, or Arabian Camel (Camelus dromedarius) no special status 
Llama (Lama glama) no special status 
Guanaco (Lama guanicoe) special concern
Vicuna (Vicugna vicugna) endangered

任务二:创建一个XML文档

为了示范每一模块是如何从其他数据源中创建新的XML文档,我们将写一个小脚本将一个简单的Perl hash转换为一个简单的XHTML文档。hash里包含一些指向很cool的特定相关骆驼的网页的URLs。

Hash 如下:

my %camelid_links = (
    one   => { url         => '
    http://www.online.discovery.com/news/picture/may99/photo20.html',
               description => 'Bactrian Camel in front of Great ' .
                              'Pyramids in Giza, Egypt.'},
    two   => { url         => 'http://www.fotos-online.de/english/m/09/9532.htm',
               description => 'Dromedary Camel illustrates the ' . 
                              'importance of accessorizing.'},
    three => { url         => 'http://www.eskimo.com/~wallama/funny.htm',
               description => 'Charlie - biography of a narcissistic llama.'},
    four  => { url         => 'http://arrow.colorado.edu/travels/other/turkey.html',
               description => 'A visual metaphor for the perl5-porters ' .
                              'list?'},
    five  => { url         => 'http://www.galaonline.org/pics.htm',
               description => 'Many cool alpacas.'},
    six   => { url         => 'http://www.thpf.de/suedamerikareise/galerie/vicunas.htm',
               description => 'Wild Vicunas in a scenic landscape.'}
);
而我们所期望从hash中创建的文档例子为:

<?xml version="1.0">
<html>
  <body>
    <a href="http://www.eskimo.com/~wallama/funny.htm">Charlie - 
      biography of a narcissistic llama.</a>
    <a href="http://www.online.discovery.com/news/picture/may99/photo20.html">Bactrian
      Camel in front of Great Pyramids in Giza, Egypt.</a>
    <a href="http://www.fotos-online.de/english/m/09/9532.htm">Dromedary
      Camel illustrates the importance of accessorizing.</a>
    <a href="http://www.galaonline.org/pics.htm">Many cool alpacas.</a>
    <a href="http://arrow.colorado.edu/travels/other/turkey.html">A visual 
      metaphor for the perl5-porters list?</a>
    <a href="http://www.thpf.de/suedamerikareise/galerie/vicunas.htm">Wild
      Vicunas in a scenic landscape.</a>
  </body>
</html>
良好缩进的XML结果文件(如上面所显示的)对于阅读很重要,但这种良好的空格处理不是我们案例所要求的。我们所关心的是结果文档是结构良好的/well-formed和它正确地表现了hash里的数据。

任务定义完毕,接下来该是代码例子的时候了。

XML Perl专用接口例子

XML::Simple

最初创建用来简化读写XML格式配置文件的XML::Simple, 在转换XML文档和Perl数据结构之间没有另外的抽象接口。所有的元素和属性都可以通过嵌套的引用直接读取。

Reading

use XML::Simple;

my $file = 'files/camelids.xml';
my $xs1 = XML::Simple->new();

my $doc = $xs1->XMLin($file);

foreach my $key (keys (%{$doc->{species}})){
   print $doc->{species}->{$key}->{'common-name'} . ' (' . $key . ') ';
   print $doc->{species}->{$key}->{conservation}->final . "\n";
}

Writing

use XML::Simple;

require "files/camelid_links.pl";
my %camelid_links = get_camelid_data();

my $xsimple = XML::Simple->new();

print $xsimple->XMLout(\%camelid_links,
                       noattr => 1,
                       xmldecl => '');
这数据到文档的任务的条件要求暴露了XML::Simple的一个弱点:它没有允许我们决定hash里的哪个key应该作为元素返回和哪个key该作为属性返回。上面例子的输出虽然接近我们的输出要求但还远远不够。对于那些更喜欢将XML文档内容直接作为Perl数据结构操作,而且需要在输出方面做更细微控制的案例,XML::Simple和XML::Writer配合得很好。

如下例子说明了如何使用XML::Write来符合我们的输出要求。

use XML::Writer;

require "files/camelid_links.pl";
my %camelid_links = get_camelid_data();

my $writer = XML::Writer->new();

$writer->xmlDecl();
$writer->startTag('html');
$writer->startTag('body');

foreach my $item ( keys (%camelid_links) ) {
    $writer->startTag('a', 'href' => $camelid_links{$item}->{url});
    $writer->characters($camelid_links{$item}->{description});
    $writer->endTag('a');
}

$writer->endTag('body');
$writer->endTag('html');

$writer->end();

XML::SimpleObject

XML::SimpleObject 对XML数据使用回想文档对象模型(Document Object Model)的存取器/accessor来提供一个面对对象接口。

Reading

use XML::Parser;
use XML::SimpleObject;

my $file = 'files/camelids.xml';

my $parser = XML::Parser->new(ErrorContext => 2, Style => "Tree");
my $xso = XML::SimpleObject->new( $parser->parsefile($file) );

foreach my $species ($xso->child('camelids')->children('species')) {
    print $species->child('common-name')->{VALUE};
    print ' (' . $species->attribute('name') . ') ';
    print $species->child('conservation')->attribute('status');
    print "\n";
}

Writing

XML::SimpleObject 没有通过抓取来创建XML文档的功能。但是与上面的XML::Simple例子一样,可以通过与XML::Writer配合简单的完成任务。

XML::TreeBuilder

XML::TreeBuilder 包由两模块组成:XML::Element用来创建和获得XML元素点的内容和XML::TreeBuilder作为一个工厂包从已有XML文件中简化文档树的创建。对于那些已有值得尊敬的 HTML::Element 和 HTML::Tree 模块使用经验的人来说,使用 XML::TreeBuilder 是非常容易的,因为除了XML特有的方法外其他都是一样的。

Reading

use XML::TreeBuilder;

my $file = 'files/camelids.xml';
my $tree = XML::TreeBuilder->new();

$tree->parse_file($file);

foreach my $species ($tree->find_by_tag_name('species')){
    print $species->find_by_tag_name('common-name')->as_text;
    print ' (' . $species->attr_get_i('name') . ') ';
    print $species->find_by_tag_name('conservation')->attr_get_i('status');
    print "\n";
}

Writing

use XML::Element;

require "files/camelid_links.pl";
my %camelid_links = get_camelid_data();


my $root = XML::Element->new('html');
my $body = XML::Element->new('body');
my $xml_pi = XML::Element->new('~pi', text => 'xml version="1.0"');
$root->push_content($body);

foreach my $item ( keys (%camelid_links) ) {
    my $link = XML::Element->new('a', 'href' => $camelid_links{$item}->{url});
    $link->push_content($camelid_links{$item}->{description});
    $body->push_content($link);
}

print $xml_pi->as_XML;
print $root->as_XML();

XML::Twig

XML::Twig 与其他只有Perl的XML接口不同,它是一个除了标准的XML APIs外还有其它富有创造性特点的Perlish接口。需要更多更详细的介绍请查看XML.com 文章

Reading

use XML::Twig;

my $file = 'files/camelids.xml';
my $twig = XML::Twig->new();

$twig->parsefile($file);

my $root = $twig->root;

foreach my $species ($root->children('species')){
    print $species->first_child_text('common-name');
    print ' (' . $species->att('name') . ') ';
    print $species->first_child('conservation')->att('status');
    print "\n";
}

Writing


use XML::Twig;

require "files/camelid_links.pl";
my %camelid_links = get_camelid_data();

my $root = XML::Twig::Elt->new('html');
my $body = XML::Twig::Elt->new('body');
$body->paste($root);

foreach my $item ( keys (%camelid_links) ) {
    my $link = XML::Twig::Elt->new('a');
    $link->set_att('href', $camelid_links{$item}->{url});
    $link->set_text($camelid_links{$item}->{description});
    $link->paste('last_child', $body);
}

print qq|<?xml version="1.0"?>|;
$root->print;
这些例子举例说明了这些普通XML Perl模块的基本使用方法。我的目标是提供足够多的例子让你感受怎么用每个模块写代码。下个月我们将关注“实现某一标准XML API的模块”,特别说明的,XML::DOM, XML::XPath 和其他大量的 SAX 和类SAX模块。

Resources


如何向CPAN上传模块

16 December 2004


  1. First Step, register. https://pause.perl.org/pause/authenquery?ACTION=request_id
    注册后你需要等待PAUSE(The Perl Authors Upload Server)小组的审核。
    我是差不多一天就收到Email,邮件里有你账号的密码。按照04pause.html文档,最迟需要三周时间。
  2. 有密码后登陆PAUSE, 登陆后如果需要上传模块,点击左侧列表的“Upload a file to CPAN”。上传一般为tar.gz模块包,它会自动解压缩的。
  3. 上传模块有好几种方式,PAUSE介绍得很清楚。其他的功能也都有介绍。不需要我的赘言吧。
如果不知道如果制作tar.gz模块包,请参考我的文章“Module::Build”与“使用h2xs打包tarball”。
我上传了Lingua::Han2PinYin, 我的CPAN目录
暂时好像没什么好写的。先就此打住。以后有情况再补充。

名词用途解释

Register Namespace/命名空间:
当你上传模块后,该模块只会放在你的目录下,不会放到CPAN的分类目录下。这时候通过命令行(perl -MCPAN -e "install XX;")是无法获取安装你的模块的。
这时候你就需要去申请注册“Register Namespace/命名空间”来防止模块名称和别人冲突,并把你自己的模块归类到CPAN的某一category/分类下。
成功后,你的页面http://search.cpan.org/~yourname下就有两块,一块是Distribution,你上传的模块;另一块是Registered Module,已注册成功的模块。

Refer/参考


用Module::Build制作CPAN模块包

16 December 2004


Problem

想将Lingua::Han2PinYin传到CPAN上。
在传到CPAN之前得先制作可安装的Lingua-Han2PinYin-0.01.tar.gz
在Win32下用ExtUtils::MakeMaker总不成功。然后就到处去找帖子看别人是怎么上传模块到CPAN的。
后来在http://www.perl.com/pub/a/2003/02/12/module1.html找到“Module::Build”,耐着性子看完了。按照操作弄弄竟然也成功了。

Solution

不想翻译那篇长文,写写自己的操作过程。
  1. cpan Module::Build
    安装Module::Build模块
  2. 写Build.PL
    use Module::Build;
    
    Module::Build->new(
        create_makefile_pl => 'passthrough',
        module_name => 'Lingua::Han2PinYin',
        license => 'perl',
    )->create_build_script;
    
  3. 上面的 create_makefile_pl => 'passthrough', 会自动生成 Makefile.PL
  4. 将Han2PinYin.pm放到当前文件夹的lib/Lingua下。
  5. cmd
    C:>perl Build.PL
    C:>perl Build
    C:>perl Build manifest
    C:>perl Build dist
    C:>perl Build realclean
    
到此,文件夹里就会生成Lingua-Han2PinYin-0.01.tar.gz。差不多就这样。很easy的。

Refer/参考


关于open的"+<"模式

13 December 2004


描述

很多人知道open除了<, >, >>模式外还有其他模式如"+<", 但有些人不太会用,或者说错误的用了"+<"模式。
一般我们更新文件(需要读取原内容文件)时,都先open<读取再对内容进行变化,最后open>写入。
而用+<加seek可以实现文件的读写更新,不用两次open,从而提高效率。
但值得注意的是:如果不用truncate,文件会出现你所不想要的后果。而另人遗憾的是书中说truncate不是所有平台都支持的。不过就我测试而言,Win2000/XP+Linux下都是可以的。
一切用实验来说话。具体原理可以参考perldoc或书籍。

实验

实验材料:程序目录下有一文本文件1.txt, 里面的内容为简单的“123456789”。

实验一:简单的计数器增加

原代码:
open(FH,"1.txt");
flock(FH, 1);
my $count = <FH>;
close(FH);
$count++;
open(FH,">1.txt");
flock(FH, 2);
print FH $count;
close(FH);
改写后的代码:
open(FH,"+<1.txt");
flock(FH, 2);
my $count = <FH>;
seek(FH,0,0);
print FH ++$count;
close(FH);
简单的从两次open转化为"+<"模式就是这个样子。

实验二:不用truncate的不可意料错误。

这种错误发生在后来写入的长度小于原文件的长度。
比如我们的任务是去掉1.txt中所有的1。如下代码是错误的,出现的结果不是我们想要的。
open(FH,"+<1.txt");
flock(FH, 2);
my $count = <FH>;
$count =~ s/1//sg;
seek(FH,0,0);
print FH $count;
close(FH);
我们所要的结果是23456789,而实际运行后的结果为234567899。后面多了一个9。
"+<"模式是覆盖模式,你后来输入的长度只覆盖那么长,如果原来文件的长度比输入的长度长,后面将会保留。
正确的代码为:
open(FH,"+<1.txt");
flock(FH, 2);
my $count = <FH>;
$count =~ s/1//sg;
seek(FH,0,0);
truncate(FH, 0);
print FH $count;
close(FH);
seek到文件头后将文件清空,再输入改变后的值。
当然有时候是不需要truncate的,看具体的要求具体分析。比如我们就要覆盖那么长后面的就要保留。
比如最上面的$count就不需要,因为加后的count不会小于原来的长度。

题外话

在写本文之前,我在看LeoBBSx的最新版本代码。LeoBBSx有用了+<模式(虽然不是很彻底,只部分采用),但遗憾的是没有truncate。
我没在本地做测试看它会不会出错,但我总觉得奇怪,它用这个模式的时候难道改变后的值都长于原来的值?
或许不用truncate是采用这模式不彻底的原因。

从我出生来过去多久了?

10 December 2004


简单描述

近日闲来无事,写个小程序冶冶情。
程序的用途是看看从我出生以来过了多长时间了?虽然我有年的概念可没有秒分时天的概念。
程序很简单,大部分脏累的工作都由Time::Local完成了。最核心的代码为:
my $in_time = timelocal($sec,$min,$hour,$day,$mon,$year);
timelocal可以把我们所能看懂的年月日等换回1970年到现在的秒,它的功能与localtime正好反一下。
写的这么长就因为要写成Web格式。麻烦了点。
可以点击http://www.1313s.com/cgi-bin/App.cgi?q=time来看看你已经活了多少秒?

Code

#!/usr/bin/perl
use strict;
use warnings;
use CGI::Carp qw(fatalsToBrowser);
use CGI qw/:cgi/;
use Time::Local qw/timelocal/;

my $cgi = new CGI;
print $cgi->header(-charset=>'gb2312');

&interface unless ($cgi->param('year'));

my $sec = $cgi->param('sec');
my $min = $cgi->param('min');
my $hour = $cgi->param('hour');
my $day = $cgi->param('day');
my $mon = $cgi->param('mon');
my $year = $cgi->param('year');
$sec =~ /^[0-9]{1,2}$/ and $sec >= 0 and $sec < 60 
	or &interface("秒");
$min =~ /^[0-9]{1,2}$/ and $min >= 0 and $min < 60 
	or &interface("分");
$hour =~ /^[0-9]{1,2}$/ and $hour >= 0 and $hour < 24 
	or &interface("时");
$day =~ /^[0-9]{1,2}$/ and $day > 0 and $day < 32 
	or &interface("日");
$mon =~ /^[0-9]{1,2}$/ and $mon > 0 and $mon < 13
	or &interface("月");
$year =~ /^[0-9]{4}$/ and $year > 1970
	or &interface("年");

$mon--;
my $in_time = timelocal($sec,$min,$hour,$day,$mon,$year);
my $now = time;

&interface("年") if ($in_time > $now);

$sec = $now - $in_time;
$min = int($sec/60);
$hour = int($min/60);
$day = int($hour/24);

print "您总共过了 <b>$sec</b> 秒,<b>$min</b> 分, <b>$hour</b> 小时, <b>$day</b> 天<br>请珍惜时间!";

sub interface {
	my $error = shift;
	print qq~<h2><font color='red'>您所输入的$error有误,请查阅后再提交</font></h2>~ if ($error);
	print <<HTML;
<form>
查看从你出生以来过了几秒几分几小时几天。<br>
请输入您的生日:<br>
<input type=text name=year size=4 value='$year'>年
<input type=text name=mon size=2 value='$mon'>月
<input type=text name=day size=2 value='$day'>日
<input type=text name=hour size=2 value='$hour'>时
<input type=text name=min size=2 value='$min'>分
<input type=text name=sec size=2 value='$sec'>秒<br>
<input type=submit></form>
HTML
	exit;
}

将汉字转为拼音的模块

06 December 2004


Update

我更新了该模块,并将它改名为 Lingua::Han::PinYin
详细的过程和用法查看:重新写就的 Lingua::Han::PinYin
下面的已经不再使用了。

说明

今天安装 Unicode::Unihan 时,在其 srctxt 目录下找到个 汉字编码与拼音对照表 / Mandarin.txt 。
另外发现在 gb2312 与 utf-8 下,汉字的编码是不相同的。
所以就重写了一遍(从函数模块转为 OO ),并把 Light 版本删除掉。
按此下载 Lingua-Han2PinYin-0.06.tar.gz

名称

Lingua::Han2PinYin - 将汉字转为它的拼音

例子

use Lingua::Han2PinYin;
  
# 如果代码的编码为 gb2312, 默认
my $h2p = new Lingua::Han2PinYin();
print $h2p->han2pinyin("我");
  
# 如果代码编码为 utf-8
my $h2p = new Lingua::Han2PinYin(format => 'utf8');
print $h2p->han2pinyin("我");

限制

不能处理多音字。

返回值

如果是一个常用字,返回它的拼音。
如果不是,返回'XX'。

应用

查询汉字的拼音:http://www.1313s.com/cgi-bin/App.cgi?q=pinyin

如何用Net::SMTP发送邮件

26 November 2004


Code

如下代码为用163.com的SMTP来发送邮件。
#!/usr/bin/perl
use Net::SMTP;

my $mailhost = "smtp.163.com"; # the smtp host
my $mailfrom = [email protected]'; # your email address
my @mailto = ([email protected]', [email protected]'); # the recipient list
my $subject = "此为标题";
my $text = "此为正文\n第二行位于此。";

$smtp = Net::SMTP->new($mailhost, Hello => 'localhost', Timeout => 120, Debug => 1);

# anth login, type your user name and password here
$smtp->auth('user','pass');

foreach my $mailto (@mailto) {
	# Send the From and Recipient for the mail servers that require it
	$smtp->mail($mailfrom);
	$smtp->to($mailto);

	# Start the mail
	$smtp->data();

	# Send the header
	$smtp->datasend("To: $mailto\n");
	$smtp->datasend("From: $mailfrom\n");
	$smtp->datasend("Subject: $subject\n");
	$smtp->datasend("\n");

	# Send the message
	$smtp->datasend("$text\n\n");

	# Send the termination string
	$smtp->dataend();
}
$smtp->quit;

TroubleShooting/Code Analysis

  • 为什么要 $stmp->auth('user','pass');
    大部分SMTP服务器为了防止 spam /垃圾邮件,就需要用户验证身份。
    此方法需要另外安装模块:Authen::SASL, 此模块可能系统不自带。
    如果系统为虚拟主机,而此模块无法安装,可使用Socket模块进行最直接的操作。详细的代码可以参考脚本LeoBBS或书籍《Perl网络编程》。
  • Debug => 1
    此段代码用于测试之用,所以开启了Debug,一般测试一次完毕,正式使用的话会关闭它。
  • 需要注意的是发信人和收信人的地址要用单引号,或者用"fayland\@gmail.com"。如果是个变量,需要用正则先将其转换。
    $mailto =~ s/\@/\\\@/;
  • 我想发送附件,该如何做?
    参考《Perl网络编程》。

邮件发送过程的简单介绍

SMTP协议由文档rfc821定义。
在rfc821协议中定义了两个角色,即发送者(用S表示,指发送邮件的程序)和接收者(用R表示,指SMTP服务器)。
  1. 在 S 和 R 通过套接连接后,S应当先向R表明身份,此过程用helo命令完成,helo后连接发送者的域名(可用localhost)。而R的回答是一个表示连接成功的状态码和服务器身份等。例如:
    S: helo 1313s.com
    R: 220 server.com Simple Mail Transfer Service Ready
    在rfc821定义的状态码中,通常以2或3开头的表示成功,以4或5开头的表示传输过程出现了问题。
    如果是需要服务器身份验证的话,还用发送AUTH LOGIN。
  2. 发送头文件。
    S: MAIL FROM: 
    R: 250 OK
    S: RCPT TO: 
    R: 
    这里的recipient的地址如果是在SMTP同一服务器上且服务器找不到此地址,就会回答"550 No such user here"。
  3. 发送正文。以DATA开始。以两个换行结束。
    S: DATA
    R: 354 Start mail input; end with (两个换行)
    S: To: [email protected]
    S: From: [email protected]
    S: subject: title
    S: ...
    S: text
    S: etc.
    S:
    S:
    R: 250 OK
  4. 退出连接。
    S: QUIT
    R: 221 server.com Service closing transmission channel

以上就是简易的连接过程。当开启Net::SMTP的debug的时候,就会输出类似于此连接过程的东西。

从细节处提升Perl性能

23 November 2004


Description

下面所写的各点都是我从其他地方找过来了。整理一下希望对各位有所帮助。

Details

变量和常量

1, 尽量减少数学表达式的计算,如
$day = 24*60*60; # 不好
$day = 86400; # better
2, 使用vec函数而不是变量来存放非常小的数字。

print

1, 如果输出里没有变量需要内插,使用单引号'而不是双引号".因为双引号会强制 Perl 检查可能插入的信息
2, 多段输出时用,而不是.。因为连接操作符.会首先将字符串连接在一起,然后将其作为一个参数打印

避免没必要的引号

摘自 FAQ 3.16: How can I make my Perl program take less memory?
没有绝对必要不要使用引号:
my $copy = "$large_string";
上述会$large_string做两次拷贝(一是拷贝到 $copy 另一次是引号内插),反之
my $copy = $large_string;
只做一次拷贝。

数组字符串化

同样摘自 FAQ 3.16
对于大数组字符串化:
{
    local $, = "\n";
    print @big_array;
}
比下面两样都更节省内存
print join "\n", @big_array;

#or

{
    local $" = "\n";
    print "@big_array";
}

引用\

如果使用大型数组或 hash 表,并使用它们作为函数的参数,那么应该使用它们的一个引用,而不应该直接使用它们。通过使用引用,可以告诉函数指向信息的指针。如果不使用引用,就需要将整个数组或 hash 表复制到该函数的调用栈中,然后在函数中再次对其进行复制。引用还可以节省内存(这可以减少足迹和管理的负载),并简化您的编程。

循环

尽早在循环内放置条件语句,以使Perl不执行无用的语句。如
while () {
    chomp;
    next if /^#/;
next可以放在chomp上面。

有选择性地使用 map 和 grep

摘自 FAQ 3.16: How can I make my Perl program take less memory?
因为 map 和 grep 是使用 LIST 列表参数,所以这么做
@wanted = grep {/pattern/} ;
会一次性读入整个文件。对于大文件来说,使用循环会更好一点:
while () {
    push(@wanted, $_) if /pattern/;
}

正则表达式

  1. pack/unpack > regexp > substr
  2. 删除字符串中的字符时用tr///d来代替s///g
  3. 在正则表达式的外面使用“or”或“||”操作。
    $found = if /one/ || /two/; # better
    $found = if /one|two/; # use above to replace
    
  4. 如果字符串很长,正则表达式很复杂,可以使用study来加快速度

数据结构

Tie::SubstrHash 对于某些类型的数据结构会有所帮助

=pod

如果您用了一大块 pod 来描述你的代码,那么请尽量不要将其放在文件的上面或中间部分。虽然 perl 分析器能很快的跳过 pod ,但是这不是魔法,它还是需要一点时间的。它还是需要从磁盘中读入它,并且读入的目的仅仅是忽略它。将所有的pod放到__END__后面,那样 Perl 编译器就不会去注意它。
但是将pod与相关代码放在一起或许是种好习惯。

warnings/strict

强烈建议编程和调试时开启,而在代码发布时去掉它们。

Final

最后强烈建议各位阅读参考里的“When perl is not quite fast enough”。

参考/Refer