|
Cultured Perl: 吸引 C 和 Java 程序员目光的 Perl 5.6 <一>
内容:
Perl 解释器
速度和 Benchmark
异常、编译和文档
Perl 的容错能力
一项任务多种实现
正则表达式
标量、数组、和哈希散列
Perl 的缺憾
Perl 的优势
”收 侧重于阐述 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、条件表达式以及
其他特征都被添加到正则表达式库中。
对于初学者的一个最好的建议就是:首先学习最基本的正则表达式
标量、数组和哈希散列
和 C、C++、Java 中变量不同的是,Perl 的变量的类型是由其名字决定的,并且会
自动初始化成相应类型。这一点让 Perl 初学者觉得很不习惯,但它非常地直观而易于
理解。
”收 推荐使用 "use strict"。通过它可以保证变量在使用之前声明,从而避免打字
错误等引起的程序错误。
如果没有做到这一点,则有可能遇到下面这样的问题:
一个常见的打字错误
$i = 5;
print $j; # print $i
此例子中,程序员本来要打印变量 i 的值,结果却敲成了 j。Perl 并不会觉得这
段代码有什么问题,它会继续执行打印语句,显示 $j 的值即什么都没有。有些时候,P
erl 的自动生成对象的确很有用,但以我的经验来说,最好还是用 "use strict" 来关
掉这一自动功能,从而避免上述问题。
Perl 变量可以是标量 (scalars)、数组 (arrays)或哈希散列 (hashes,又叫做关
联数组)。(事实上,Perl 中有多种数据类型,但是程序员并不会直接面对它们。)此
外也可以是引用,通常它们也是一种标量类型。其中标量名称以 "$" 开头,数组名以
"@" 开头,而散列则以 "%" 开头。
标量是 Perl 中最简单的数据类型。每个标量都有唯一的值,或者是字符串或者是
引用。在必要的时候,字符串和数字可以互相转化。这常让初学者觉得欣喜异常。看一
下这个例子:
标量
$i = "hi there";
print 1+$i; # prints 1
其中标量 $i 的值是字符串 "hi there",它对应的数值为 0。因此 1 + "hi
there" 的值为 1,程序运行结果为 1。
不过这并不意味着 Perl 解释器在对某个标量分别考虑其字符串类型和数字类型。
事实上,在内存中只是一个含有某个值的标量。如果在数值运算的语句中(如加法),
这个标量值就转化成数值形式;如果在字符串操作语句中(例如打印),则以字符串形
式执行。但无论以什么形式运算,该标量变量实质上只有一个值。
未定义的标量的值为 "undef"。如果在 C/C++/Java 程序中,您可以将其他值与
null 比较,但在 Perl 中却不能拿任何东西来与 "undef" 做比较。可用这样使用
defined() 函数:
Use of the 'defined()' function $i = "hi there";
print $i if defined $i; # prints "hi there"
undef $i; # set $i to be undef
print $i if defined $i; # prints nothing
数组实质上就是一组标量。如果需要,数组大小可以自动改变,有点象 Java 中的
Vector 类。C 和 C++ 中没有与 Perl 数组类型相当的东西,但它们也有一些提供类似
功能的库(如 STL)。数组的一个有趣特性在于,数组的标量数值等于它的元素个数:
数组中的元素个数
@a = ("hi there", "nowhere");
print scalar @a; # prints 2
push @a, "hello"; # add "hello" at the end
print scalar @a; # prints 3
散列与数组类似,但里面的标量并不是按照位置排序的,而是通过另一个标量(必
须是唯一值)来进行索引。例如一个用 social security number 作索引的名字列表就
是一个散列。将某个键值插入到散列后,该散列会自动扩展。散列与 Java 中的
HashMap 和 Hashtable 类很相似。
引用类型其实也是标量,它们类似于 C 语言中的指针,能够指向任何东西。这就允
许 Perl 生成一个散列的数组、数组的散列、散列的散列、或是数组的数组(多维数组
)。有多种方法来获得引用所指向的内容,或者直接使用引用的名字、或者使用 "->"
操作符。引用是一个涉及范围非常广的问题,可以参考 "perldoc perlref" 参考文档来
获得更多相关信息。
C 和 C++ 只有一些固定类型的标量。当程序员要使用数组或哈希散列时,不得不去
使用钩子 (hoop) 或是 STL 等外部库。
Java 中有相当于 Perl 里数组或散列功能的类库,但它们在 Java 语言中并不是那
么直接。比如说要对散列上所有元素做操作所需要的时间大约是 Perl 的三倍。
对散列中所有元素进行操作的 Java 代码
import java.util.Enumeration;
import java.util.Hashtable;
Hashtable hi = new Hashtable();
// fill in hi's values
// we can use an Iterator, still a lot of typing
for (Enumeration enum = hi.elements();
enum.hasMoreElements();)
{
Object o = enum.nextElement();
// do something with o
}
对散列中所有元素进行操作的 Perl 代码
# note that this even includes the definition and initialization of
# the hash, and still is more compact than the Java code!
%hash = { a => "hi", b => "hello" };
foreach (values %hash)
{
# do something with $_
}
Perl 的缺憾
Perl 缺少 C、C++ 和 Java 中的许多特性,但它毕竟是一门完全不同的语言。这几
种语言中甚至有许多特性是互相矛盾的。例如 Java 只支持单一继承,而 C++ 则可以有
多个父类。这种有冲突的情况下当然不可能继承所有语言的特性,Perl 有它自己处理问
题的方法。
由于 Perl 程序能够被连接到 C 的库中(事实上,这也就是 Perl 应用广泛的原因
之一),这就使得几乎没有任何 C 或 C++ 能做而 Perl 不能的事情。
与 C 和 C++ 相比而言,Perl 有时欠缺的是运行速度。这的确是一个问题,但是通
过良好的编程算法以及 Perl 内置函数的使用,就能够克服这一缺点。
Perl 还不能直接使用 C 和 C++ 的库。必须通过不同的模块和绑定,才能够将这些
库中的常量以及函数功能转化成适应 Perl 的样子。这就会导致开发和程序运行的速度
降低。但由于 CPAN 库中发布了大量这些方面的模块,因此这个问题并不是那么难以解
决,
在训练编程技巧方面,Perl 并不象 C 和 C++ 那样深入人心。它是一门年轻的语言
,虽然很受欢迎,但并未被人们普遍接受。然而,大多数的 UNIX 系统上都安装了
Perl,且其他的操作系统也都支持 Perl。
Perl 支持单一继承或多继承、封装以及多态,但这仅仅是通过外部模块或程序员的
协同来实现的。也就是说,Perl 语言本身并没有严格的面向对象编程规则,需要程序员
自己来实现面向对象。这一点有好也有坏,这就要取决于程序员或项目本身了。
Perl 的线程以及统一字符编码(Unicode)支持远远落后于 Java,也稍微次于
C/C++。Java 从最开始设计就支持线程和 Unicode,而 C/C++ 则比 Perl 拥有更多的时
间来调整这方面的正确支持。在 Perl 中,对线程和 Unicode 的支持仍处于起步阶段,
但 5.6.0 之后的稳定版本发布之后这一点将得到改观。
Perl 的优势
对于 C/C++/Java 程序员而言,Perl 在某些方面的优势是无价的。例如正则表达式
在 Perl 中的实现是轻而易举的,但在 C、C++ 或 Java 中实现起来却很麻烦。隐含的
函数声明、不严格的语法、以及象日用文档似的程序结构使得 Perl 更具吸引力。
Perl 并不适合于所有人。它需要读者去适应,却接受它的所有缺点和优点。我们并
不是觉得 Perl 酷才采用它,而是因为它的确是一种非常好的工具。如果在解决某个问
题时使用其他语言更合适,那么就应该放弃 Perl。一个好程序员的手头总是有好几种有
用的工具。
Perl 有一些小的不足,但那些不知疲惫的程序员会忽略掉这些缺点。如果的确需要
线程和 Unicode 支持,或是严格的面向对象编程,那么你只好根据这些需要来选择其他
更合适的语言了。
Perl 是一门通用的灵活的语言,可以象胶水一样将其他许多不同的模型粘合起来。
它能够实现任何过程或函数的算法。通常情况下,Perl 会大大减少开发的时间,因为它
对某些常见的任务(例如对散列表中的所有元素做操作)只需要少量的代码。最重要的
是,Perl 编程总是相当于一个有趣的学习过程。
资料
在 CPAN 上可以找到所有你想要的模块。
查阅这几页 perldoc 帮助文档: perlrun, perlsyn, perlfaq, perlop,perlcc,
perlre, 以及 perlref。
Perl 的作者,Larry Wall 所著的 "Natural Language Principles in Perl"。
参考 Perl.com 站点上的 Perl 相关信息和资源。
Programming Perl, 3rd Edition, Larry Wall, Tom Christiansen, 和
JonOrwant 著 (O'Reilly & Associates, 2000 出版)。这是目前最好的 Perl 指南,并
更新了 Perl 5.005 和 5.6.0 版本方面的内容。
Mastering Regular Expressions, Jeffrey E. F. Friedl 著(O'Reilly
&Associates, 1997 出版)。此书着重于探讨正则表达式方面的所有内容。
如果您渴望进一步了解 Perl 技术内幕,可以参考 Larry Wall 的主页.
Perl.org, 另一个重要的 Perl 主页
Republic of Perl home page是一群致力于 Perl 的程序员的家园
网上预定 O'Reilly 的 Perl Conference。 |
|