Perl和Python的比较研究
随着系统管理的复杂化和网络运用的扩展,脚本语言在实际编程中的应用越来越广泛。传统观念是:一般的高级语言如C/C++,Java,Delphi等用来编写主要的功能组件,如java的类和beans,用C/C++写的动态连接库,用VisualBasic写的控件等,而脚本语言如JavaScript,Vbscript,perl,python,sh等通常认为脚本语言是介于HTML应用的发展和脚本语言本身的发展,脚本语言的应用早就超出仅仅作为常规编程语言的辅助角而用来直接开发应用系统,著名的网络流控制系统mrgt就是用perl开发的。现在的脚本语言,其功能甚至强大到可以和一般高级语言相媲美,而且引入较新的程序机制和技术(如OO和异常处理),加上固有的简单易用,逐渐发展为程序开发的一支主流。脚本语言的基本特征是语法简单,跨平台,类型宽松,解释执行。早期的脚本语言?本文选择现今在自由软件开发里很具代表性和广泛使用的两种脚本语言perl和python进行比较研究,以期使读者能对这两种脚本语言的基本特点和新发展有一定的了解。
一、 两者初识
Perl(可以看作Practical Extraction And Reporting Language的首字母)语言最早由Larry Wall开发,原始动机即作为一个文本提取和报告的实用语言,本来是基于UNIX系统,后来发展成能运行于几乎所有的机器体系和操作系统。Larry Wall是坚实的免费软件拥护者,所以perl也成为免费软件之一(但不属GNU),按自由免费软件的一般模式发展和传播(perl中的源代码、标准库文件、可选模块及所有文档都免费提供,并
被用户团体所支持)。从1988年的最初诞生,到现在的perl 6系列版本,perl能够如此稳健蓬勃的发展是和它自由免费、简单高效(语法上象C和Unix的sh,解释执行前会简单编译,有点象java)、强可扩展性、数据类型灵活、面向对象以及有强大规范的用户团体交流(CPAN, Comprehensive Perl Archive Network)和幕后支持组织(CPAST, Comprehensive Perl Arcana Society Tapestry)分不开的。
Python最初出现在2000年前后,名字来源一喜剧团体Monty Python,并无实际代表意义。Python最初由Guido van Rossum及其合作者设计开发,后来python开发小组多次重组,最终到Digital Creations。Python和perl一样是在迅速稳定发展,目前的一个著名成功业绩是Digital Creations开发的zope,下一代开放源码应用服务器以及门户工具。从抽象的功能和特点来说,python是和perl 最相像的语言,可能和perl的成功和python的较晚出现有关。和perl一样,python 也是自由免费、简单高效、强可扩展性、数据类型灵活、面向对象的。并且python 的功能相对更强大,更易于编写大程序,核心又较小。尽管从抽象的角度,perl 和python两者有很大的相似,但作为不同的语言,他们却是又有许多差别,下文从几个主要的方面对两者进行深入的比较研究,尽量能出它们的异同并对它们一些进行原理和模型层次的探讨。
下面我们先来看一下如何用这两个语言实现最简单的“hello!”程序,先对它们有个大概的印象。在perl情形,先选择一个你比较喜欢的文本编辑器编写hello.pl程序如下:
#This is a hello saying demo
print “what is your name?\n”;
$name=<STDIN>;
print “hello $name!”;
在终端或命令行输入perl hello.pl或直接输入hello.pl程序将被解释执行,提示输入你的名字,输入名字xiao rong后程序在屏幕输出“hello xiao rong!”。
Python脚本运行有两种方式,一种是交互式的,一种是自动运行脚本。交互式是在命令行输入python,然后进入交互状态(>>>为主提示符,…为从提示符):ActivePython 2.4.1 Build 245 (ActiveState Corp.) based on
Python 2.4.1 (#65, Mar 30 2005, 09:33:37) [MSC v.1310 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> name=raw_input("what is your name?\n")
what is your name?
xiao rong
>>> print "hello, ",name,"!"
hello, xiao rong !
>>>
或者编好脚本文件hello.py如下:
name=raw_input(“what is your name?\n”)
print "hello, ", name, "!"
python 定义数组然后在命令行输入python hello.py或hello.py发生的情形和perl一样。
所以从顶层模型来看,perl是一个完整的perl脚本输入自动执行。一个perl 脚本文件和C/C++源程序类似,由一些语句和语句块组成,语句由“;”分隔,语句块由一对“{}”包括。而python脚本既可自动执行,又能交互式运行。而且python特别的一个地方使它采取缩进来标示分界和层次,不同于一般语言采用”;”和”{}”。这种缩进方式的程序在运行时依赖一个栈来记录逻辑行的INDENT (缩进)和DEDENT(抽
出)。在读入脚本文件第一行之前,有个数值0压入栈底,这个0直到脚本运行完是不会被弹出的。压进栈中的的数值是从底向顶严格增加的。每次读到一行新的逻辑行,先比较它的缩进量和栈顶纪录的缩进量大小,大则表示进入下一层,此时把新的值压入栈并产生一个INDENT记号,小则表示此缩进量和前面某处一样(否则错误,这一点很严格),这时把栈中比它大的量都弹出,产生一个DEDENT记号。如此可以获知每一行所在的层次,并逐行解释执行。
二、 数据类型
脚本语言特点之一就是数据类型灵活,变量无需先声明,类型是靠值来动态确定的。一个变量在一个程序中可以存储不同类型的值。
Perl支持的内置数据类型有数值(包括整数和浮点)、字符串(包括单个字符)、引用、列表和数组、关联数组(字典),其中前3类属标量,在变量名前面由”$”标示,列表和数组变量名由”@”标示,关联数组名由”%”标示。即变量类型分三类,标量($varname),列表(@varname),关联数组(%varname)。之所以采取这样的标示,很可能是和perl要支持引用相关,因为,要高效支持引用,就得很方便的知道引用的类型。内置数据类型有内置方法支持,如数值的+-*/,字符串的连接、比较和匹配(详见下文的正则表达式讨论),列表的排序和翻转。当然
除了内置的类型,还支持新类型,面向对象的自定义对象,在面向对象分析小节使我们详细讨论。Perl
的大部分数据对象采取值模型,赋值时复制对象。所以它把引用类型引入(类似C里的指针,但作为引用类型不能进任何算术运算),以方便数据的传递,在子程序小节将讨论引用的应用。引用类型属标量,通过在普通变量前加”\”来获得引用的值,如$rarr=\@array;取值时用类型标示符作用在引用变量前即可,如@$rar得到数组@array的值,而$$rarr[i]取到@array第i元素(数组下表从0开始)。
Perl的文件管道句柄用标量来存储,这样很方便对文件和管道的操作。另外,perl支持别名(alias),即在变量前加“*”,如:
@array=(1,2,3);
*arr=*array;
$arr[0]=2;
print "\@array:@array\n\@arr:@arr\n";
这里有的一个问题是,如果同时有@array,$array,%array变量则解释器无法区分它们的别名。别名实际是指向符号表项的指针,可以用来方便文件句柄和列表的操作。别名在perl中又叫类型标识。
Python支持的内置基本类型有数值、字符串、链表、元组、字典。由于设计时把python定位为完全的面
向对象(包括数值类型,直接支持复数类型),对对象操作大多都是靠调用对象的方法来实现。Python的不同类型变量定义无需特殊标示。和perl不一样,python大部分对象采用引用模型,但是python不支持引用型变量。象这类采用引用模型的语言,对对象一般分成两类,可以修改(mutable)和不可修改(immutable),不可修改的对象如果发生修改操作报错(但python返回”NotImplemented”型值)。
两者的列表数组和链表意义一样,是一个可以同时存多种标量值(整数,字符串等等)的一个线性表。Python的链表允许插入、删除、倒置、排序、切片等操作,长度动态变化;perl的数组只允许在切片和首尾增加删除元素操作。两者的关联数组和字典大致一样,存储的是健值对,允许检索,插入,删除。Python 比perl多的元组是不可修改型(不能在其中增加、删除、重新赋值)可以当作列表常量(在这一点上和perl的列表更象,它们的输出形式都是“(item1,item2,…,itemn)”,python的链表数组形式为[item1,item2,…,itemn]),而且Python的元组允许嵌套,相当于广义表(但不允许原地插入删除)。
三、 控制流
perl支持灵活多样的控制流。有多种条件判断和循环并且支持循环控制和goto跳转。首先说一下perl里的条件和条件表达式。同C语言一样,perl没有另外的boolean类型,零和空等表示false,其余为ture。条件可能来自比较,函数返回(如读取),状态和环境的判断(如文件是否存在)。特别注意在perl中的有些比较操作,返回3真值1,0,-1。如数值比较的”<=>”运算符和字符串比较的cmp操作。逻辑运算有与(&&或and连接)、或(||或or连接)、非(!或not)、异或(xor)。
Perl除提供传统的if (<expression>) { <statement_block_1> } elsif (…) { <statement_block_2> } … else{ <statement_block_3> }条件控制外,还提供了特别的单行条件语句,语法为statement keyword condexpr。其中keyword可为if、unless、while或until,如: print ("This is zero.\n") if ($var == 0) 和 print ("This
is zero.\n") unless ($var != 0),虽然条件判断写在后面,但却是先执行的,只有满足条件才执行前面的语句。
Perl支持while循环 while ( <expression> ) { <statement_block> },until循环 until ( <expression> ) { <statement_block> },类C的for循环,针对列表(数组)每个元素的循环foreach,语法为: foreach localvar (listexpr) { statement_block; }(此处的循环变量localvar是个局部变量,如果在此之前它已有值,则循环后仍恢复该值。在循环中改变局部变量,相应的数组变量也会改变),do循环 do { statement_block } while_or_until (condexpr); do循环至少执行一次循环。
退出循环为last,与C中的break作用相同;执行下一个循环为next,与C 中的continue作用相同;Perl特有的一个命令是redo,其含义是重复此次循环,即循环变量不变,回到循环起始点,但要注意,redo命令在do循环中不起作用。Goto语句和C完全一样。
Python的控制结构相比方式少一些,但能满足通常的需要(因为支持while 型的循环)。Python的条件
表达式值有Booleans类型值True和False表示,但是Booleans是作为Integers即整数的子类型,其中Ture就是1,False就是0。Python 的条件值同样由比较,函数,环境和状态产生。有与(and)、或(or)、非(not)运算。Pyhton支持多分支的if…elif…else条件判断,但书写时必须注意缩排对齐。Python支持所谓的对序列中(包括链表和元组)的元素的for循环“for elements in Sequence:”,此时要特别注意在循环过程中不应改变列表形状;python还支持while循环,while expression : block [ else : block ]。Python 循环时除支持类似C 中break,continue等跳转控制语句外,还有一种pass语句,它什么都不做。四、 解释和环境
很多脚本语言的运行是需要脚本解释器或专门的虚拟机(类似Java的JVM)。而且一般脚本语言不能直接生成可执行文件,所以与环境和操作系统的交互必须通过一些间接的方式,如更多的通过环境变量和特殊变量。这一节我们来看一下perl和python的解释执行与环境交互。
Perl和perl函数的执行可以被某些环境变量影响,许多环境变量在安装perl 时被操作系统或shell自动设置,有些变量可以在脚本运行时被修改和配置。Perl 通过内置变量哈希表%ENV提供到当前解释器的环境变量的接口,常用的环境变量包括HOME,PATH,PWD,HOST,USER等,环境变量在不同的操作系统下是有差别的。除了环境变量外,还有一些特殊变量可以用来控制perl运行或为perl运行提供外部信息,如@ARGV存储传递给程序的参数,@_存储传递给子程序的参数,$]表保存perl版本,$@保存最后一次调用eval产生的错误,$?保存最后一个子进程返回状态,@INC包含包搜索目录列表,%INC保存
通过do 和require得到的程序中每一个文件的列表,%SIG表示信号句柄(在第十节会详细介绍),STDERR,STDIN,STDOUT分别是标准错误、输入、输出句柄。Perl 可以通过system函数运行程序或外部命令,通过fork创建子进程。
Perl在解释执行前实际有一个粗略的编译,解释执行的是操作码(由C语言编写的一些基本操作指令,类似与Java的虚拟机)。尽管perl是以脚本语言,可以使用perlcc命令或其它工具将perl脚本编译成可执行文件(在windows下.exe 文件)。但是,编译成可执行文件不会进一步提高perl代码的执行效率,因为它只是进一步把操作码翻译成机器指令,而不会进一步的优化机器码。
Python通过sys和os两个模块来和系统与环境进行交互,其中sys模块处理一些与具体系统无关的,而os是针对具体系统的。在sys模块的argv数组中可以获得命令行参数,stdin、stdout、stderr分别获得标准输入输出错误的句柄,可以通过sys里的函数exit终止脚本的运行。另外sys模块还提供了一些解释器的信息,如modules是当前被加载的模块,path是个字典,是模块和路径对。os 模块的主要功能是确认用户和进程环境,对诸如文件和文件系统这样的外部系统的进行控制和通信。os中的字典environ提供对当前进程的环境变量的访问,函数system和exec用于启动其它程序或外部命令。关于进程的控制和通信在第十节并发控制将进一步讨论。
Python在解释前也是会进行编译的,这一点在使用import语句导入一个的模块时看得更清楚,因为它将
源代码”.py”编译成”.pyc”再导入,每次导入都会比较”.py”和”.pyc”文件的时间戳,确定是否需要重新编译。Python的解释器构架中有一部分就叫做PVM(python虚拟机);Python脚本解释器核心比较小,很容易用其他高级语言实现,如现在比较流行的Jython,可以看成java和python的一个结合。
五、 正则表达式
本来正则表达式一般不在的程序语言设计时考虑。但考虑到脚本语言,尤其是早期的脚本语言如awk,很大程度上是为加强在Unix Shell上的报文处理而设计。从这个程度上来说,perl的内置强大正则表达式处理能力更有传统脚本语言的风格。正则表达式处理一般包括模式串的匹配、提取、替换、分割。而模式串用一个正则表达式来描述。如一般语言里的标示符用[a..z]|[A..Z]( [a..z]|[A..Z]|[0..9])*表示以字母开头任意个字母或数字的字符串。
Perl语言支持对字符串进行模式匹配、替换等丰富的正则表达式相关操作,使用非常方便。和通常郑泽表达式记号差不多:在一对”/”里表示模式串,即/pattern/;”.”代表任一字符,”*”表示前一字符的任意重复,”+” 表示前一字符的至少一次重复;[…]中表示字符可选集,[^…]表示禁止字符;特殊字符用”\”转义,”|”表示选择项,{n}指定n次重复等等。“=~”是模式匹配符,表达式$str=~/pat/意思是判断$str里面是否存在”pat”串,返回第一个匹配的串(有的话为非零值即真),可以用它给一个数组赋值,@match=$str=~/pat/g(”g”选项表示匹配所有可能)。下面举一个复杂点例子来说明正则表达式的使用,在一个文本文件里出所有的emial地址(x)输出:
#prntemail.pl 从email.data文件中出所有的email地址
$file="email.data";
if (open($dfile,"$file")){ #打开文件
@data=<$dfile>; #读数据
foreach $line (@data){
print"@email\n" if(@email=$line=~/[^ \\\.]+\@[^ \\\.\@]+\.[^ \\\.\@]+\.[^ \\\.\@]+/g);
#上一行语句为从每行中出所有的x形式的串并输出
}
close($file);
}
else{print "error in open file $file\n"}
另一常见用法是用split(/pat/, $str) 将字符串用某模式分成多个单词。此外,=~
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论