乘风原创程序

  • Cultured Perl: 吸引 C 和 Java 程序员目光的 Perl 5.6
  • 2007/5/10 8:05:04
  •  Perl 5.6 的新特性在哪些方面优于 C/C++/Java ?

    Teodor Zlatanov
    Programmer, Northern Light, Inc.
    January 2001

    笔者侧重于阐述 Perl 与 C 或 Java 不同的独特之处。您一定会为 Perl 这些在其他语言中看不到的特性而心花怒放:操作符的容错能力、一项任务多种实现、标点、正则表达式以及变量机制等。所有这些都赋予您的手指更灵活的魔力。在某些方面 Perl 的确能给 C 和 Java 程序员很多有用帮助,可惜目前它还远达不到众所周知的程度。因此,抓紧机会提高您的 Perl 水平吧!

    Perl 有时甚至令有经验的程序员也觉得头疼,因为他们发现一不小心就会写出模棱两可的语句。但这种在结构、特性体系方面的模糊性从另一方面显示了 Perl 语言强大的能力。毕竟 Perl 语言最开始的设计初衷就是希望能用多种方式来达到同一目的。

    这里我们将要探讨 Perl 5.6 中那些最容易混淆的特性,并将它们与 C/C++/Java 中相应的特性做比较。主要围绕 "Natural Language Principles in Perl" 中的原则展开(Larry Wall 著,参看本文末尾的 资料 部分),因为它们是 Perl 最能与 C、C++ 和 Java 语言区分开的地方。此外,关于 Perl 语法的结构可以在 "perldoc perlsyn" 参考文档中找到,另一本值得推荐的 Perl 指南读物则是 Programming Perl

    Perl 解释器
    初学者马上就会发现 Perl 中似乎没有任何编译器。事实上,Perl 脚本大多是由 Perl 解释器直接运行的,例如 UNIX 系统下的 "Perl"、DOS/Windows 下的 "perl.exe" 就是 Perl 解释器。而在 MacOS 中则不需要这些解释器。您可以试试看如何运行 Perl 脚本。首先在您的操作系统上启动相应的 Perl 解释器,或是在 MacOS 系统中直接运行。在大多数系统中,文件尾标志(UNIX 系统中为 Control-D)用来表示用户输入结束。因此在 UNIX 系统中,下面这个脚本将能得到 "5+6" 的计算结果:

    从最简单的 Perl 程序入手

    > perl
    (Perl is waiting for user input here, because no script name is given)
    print 5+6
    You press Control-D here
    11  
    


    可以看到 Perl 解释器运行了这个只有一行的脚本程序,并输入该表达式的计算结果 11。

    Perl 解释器有许多选项。例如,-e 选项表示将命令行的输入作为脚本文件来执行,因此上面的脚本例子也可以这样实现:在命令行输入 perl -e'print 5+6'(注意,要用单引号将命令括起来)。而 -i 选项则类似于通过一个过滤器,允许在文件中不同的位置进行编辑。-n 和 -p 选项能让程序员打开或关闭输出。-w 选项与 C/C++ 中的 "-Wall" 编译选项类似,都能够对程序中潜在问题给出警告信息,但与 -Wall 不同的是,-w 功能在程序运行时也是被激活的。

    速度和 Benchmark
    人们常常拿 Perl 与 C/C++ 比较,并抱怨 Perl 运行速度不够快。某些时候这的确是事实,但是并非永远如此。我建议您在认为 C 或 C++ 更快之前,使用 Benchmark 模块试试看 (perldoc Benchmark)。此外,Perl 能很方便地与 C/C++ 代码或库连接,且某些 Perl 内置函数并不比 C 代码慢,如排序或打印等。这里再次提醒您在坚信 C/C++ 比较快之前,先使用一下 Benchmark 模块。

    要记住,过早的优化往往是错误的根源。如果您在 Perl 中写了一个原型,并用其他语言来重写是没有问题的。原型意味着能够方便地开发。

    与 Java 相比而言,Perl 也能够很好地工作。Perl 不象 Java 那样擅长于线程,但它的 Tk GUI 界面工具箱却比 Java 的 Swing GUI 库要好。并且 Java 代码总是能够连接到 Perl 程序中,反之亦然。因此,有时您可以通过某种程度上的结合,使得程序在两方面都做得很好。

    异常、编译和文档
    Perl 通过 CPAN 中的模块或是其内置函数 eval() 来抛出异常。就好像在 C++ 或 Java 中通过 try/catch 代码块来处理异常一样,eval 函数能处理某个代码段或某个字符串操作的异常。

    事实上,Perl 程序在运行之前还是需要编译的,只是和 C/C++/Java 的编译方式不相同。在设计和效果上,它和 Java 的字节解释过程很相似。关于编译的更详尽内容,可以参阅 "perldoc perlrun" 和 "perldoc perlcc" 文档。

    可用 POD 格式来将文档嵌入 Perl 程序。这种文档嵌入方式比 Javadoc 格式(仅适合于 API 文档)要通用,但比不上 C/C++/Java 的注释。

    即使和 C、C++ 或 Java 比较起来,Perl 程序也不算是一种结构性强的语言。例如,BEGIN 语句块会被首先执行,但它可以在程序中多次说明。定义、变量和函数体可以在程序的任意位置出现,Perl 所提供地强大功能可以最好地满足这种随意性。

    由于这种松散结构、嵌入的注释、以及为追求方便而导致的令人混淆的语句,使得书写 Perl 程序更像是在写一封英文信件。

    Perl 的容错能力
    Perl 比 C/C++/Java 更能容忍一些模糊地写法。例如可以用逗号来分隔语句或函数的参数:

    语句之间或函数参数之间的分隔符

    print 'Hello', ' ', 'there.', "\n";     # print "Hello there\n"
    
    foreach (1..10)
    {
     my $i;
     $i = $_ * 2, print "$i\n";             # print evens from 2 to 20
    } 
    


    Perl 能尽最大可能地消除这些语句可能引起的歧义。当然,有些时候仍有无法解决的歧义(在这一点上,Perl 就象英语一样)。

    Perl 中另一个容易引起歧义的地方在于:变量经常会被隐含使用。例如,"print" 语句缺省时会打印 $_ 变量的值。在其他一些含混的语句操作中,$_ 变量也是它们的缺省值,这就造成了一种混乱。例如:

    隐含使用变量

    $_ = "hello";
    s/hello/hi/;                            # $_ is "hi" now
    print;                                  # prints "hi"  
    


    这里你可以看到,使用缺省变量能让编程方便简洁。也就是说,Perl 和英语类似,通过某种模糊性来简化表达式。

    一项任务多种实现
    (There's more than one way to do it ,TMTOWTDI)

    所有的语言在解决问题时都有自己的方法。在 C 里面,for() 循环是在一定范围内重复的最好方法;在 Java 里,静态函数的调用是直接通过类而不是某个实例。

    但对于同一件事情,Perl 至少有两种解决方法。TMTOWTDI 原则就是 Perl 的座右铭,各种处理上的差异在 Perl 编程中是深受鼓励的。

    下面来看一个打印数组元素的例子。所有的表达方法都达到同一目的。

    打印数组元素

    print foreach @array;
    
    foreach (@array) {print};
    
    map {print} @array;
    
    print @array;  
    


    要理解以上这些代码的唯一途径就是掌握所有 Perl 的语法。不要担心哪种方法才是正确的,因为实现同一目标总是有多种正确的方法。考虑这些不同的表达方式,您可以更深刻体会 Perl 的这个座右铭。

    另外,虽然一个任务的实现有多种方法,但这并不意味着所有方法都是正确的。通常情况下,更有可能写出的是一些错误代码。为了保证代码的正确性,最好尽量使用那些 Perl 内置的函数,而较少使用自己所编写的函数,并且注意证明并记录这些不那么显然的方法。

    正则表达式
    如果没有初始化,正则表达式很有可能造成一片混乱。大多数人都相信正则表达式是由 Kalahari bushmen 发明的,它渗透到了大学的计算机科学编程的所有方面。

    Perl 的正则表达式是从 shell 脚本程序以及 awk/grep 工具中继承而来的。但它的能力却远远超出了原来的模型。

    基础的正则表达式是非常容易书写的,但难以读懂。例如 "con\w+" 和 "contra"、"contrary" 匹配,但与 "pro" 或 "con" 都不匹配。然而在 Perl 5.6.0 中,正则表达式被固化了。Unicode 字符集、模式内任意代码操作、flag toggles、条件表达式以及其他特征都被添加到正则表达式库中。

    对于初学者的一个最好的建议就是:首先学习最