perl语言编程 第一章 Perl 概述 下
上一篇 / 下一篇 2008-04-18 11:36:59 / 个人分类:关于脚本语言
1.6 流程控制
到目前为止,除了一个大的例子外,所有其它的例子都是只有线性代码;我们按顺序执行代码.我们也已经接触到了一些例子中,利用”短路”操作符来使得单个命令被(或不被)执行.虽然你可以写出很多有用的线性代码(很多的 CGI 脚本就是这个类型的),但是使用条件表达式和循环机制可以写出很多更加强大的程序.它们在一起被称为控制结构.因此你也可以认为 Perl 是一种控制语言.
为了能够控制,你必须能够做决定,为了做决定,你必须知道假和真之间的差别.
1.6.1 什么是真
我们前面已经涉及到真的概念,(注:严格说,我们这么说并不正确).同时我们也提到了一些返回真或假的操作符.在进行更深入的讨论之前,我们应该给所提到真和假准确的定义.Perl 中真的判断与大多数的计算机语言中的稍微有些不同,但是通过一段时间的使用,你会对它有更多的认识(实际上,我们希望你能通过阅读下面的内容获得很多的认识).
基本上,Perl 以自明的方式处理真.这是一种灵活的方法,你可以确定出几乎所有事物的真值.Perl 使用非常实用的方法来定义真,真的定义依赖于你所处理的事物的类型.事实上,真值的种类要比不真的种类多得多.
在 Perl 中,真总是在标量环境中处理.除此外没有任何的类型强制要求.下面是标量可以表示的不同种类的真值:
- 除了””和”0”,所有字符串为真
- 除了 0,所有数字为真
- 所有引用为真
- 所有未定义的值为假.
1.6.2 If 和 unless 语句
早些时候,我们已经看到一个逻辑操作符如何起一个条件的作用.一个比逻辑操作符稍微复杂一些的形式是 if 语句。if 语句计算一个真假条件(布尔表达式)并且在条件为真时执行一个代码段。
if ($debug_level > 0) {
# Something has gone wrong. Tell the user.
print "Debug: Danger, Will Robinson, danger!\en";
print "Debug: Answer was '54', expected '42'.\en";
}一个代码段是由一对花括弧括在一起的一些语句。因为 if 语句执行代码段,因此花括弧是必须的。如果你对一些其它语言比较熟悉,例如 C,你会发现它们的不同之处,在 C 中,如果你只有一条语句,那么你可以省略花括弧。但是在 Perl 中,花括弧是必须的。
有些时候,当一个条件满足后执行一个代码段并不能满足要求,你可能要求在条件不满足的情况下执行另外一个代码段。当然你可以用两个 if 语句来完成,其中一个正好和另一个相反,为此 Perl 提供了更好的方法,在第一个代码段后边,if 有一个可选的条件叫 else,当条件为假时运行其后的代码段。(经验丰富的程序员对此不会感到惊讶。)
当你有多于两个选择的时候,你可以使用 elsif 条件表达式来表示另一个可能选择,(经验丰富的程序员可能对“elsif”的拼写感到惊讶,不过我们这里没有人准备道歉,抱歉。)
if ($city eq "New York") {
print "New York is northeast of Washington, D.C.\en";
}
elsif ($city eq "Chicago") {
print "Chicago is northwest of Washington, D.C.\en";
}
elsif ($city eq "Miami") {
print "Miami is south of Washington, D.C. And much warmer!\en";
}
else {
print "I don't know where $city is, sorry.\en";
}if 和 elsif 子句按顺序执行,直到其中的一个条件被发现是真的或到了 else 条件为止,当发现其中的一个条件是真的,就执行它的代码段,然后跳过所有其余的分支。有时候,你可能希望在条件为假的时候执行代码,而不想在条件为真时执行任何代码。使用一个带 else 的空 if 语句看起来会比较零乱。而用否定的 if 会难以理解,这就象在英语中说“如果这不是真的,就做某事”一样古怪。在这种情况下,你可以使用 unless 语句:
unless ($destination eq $home) { print "I'm not going home.\en"; }
但是,没有 elsunless。这通常解释为 if 的一个特性。
1.6.3 循环
Perl 有四种循环语句的类型:while,until,for 和 foreach。这些语句可以允许一个 Perl 程序重复执行同一些代码。
1.6.3.1 while 和 until 语句
除了是重复执行代码段以外,While 和 until 语句之间的关系就象 if 和 unless 的关系一样。首先,检查条件部分,如果条件满足(while 语句是真,until 是假),执行下面代码段,例如:
while ($tickets_sold < 10000) {
$available = 10000 - $tickets_sold;
print "$available tickets are available. How many would you like: ";
$purchase = <STDIN>;
chomp($purchase);
$tickets_sold += $purchase;
}注意如果没满足最初的条件,就根本不会进入循环。例如,假设我们已经卖出 10000 张票,我们可能希望执行下面的代码:
print "This show is sold out, please come back later.\en";
在我们前面的“平均分”例子中,第 4 行:
while ($line = <GRADES>) {这句代码将文件的下一行内容赋值给变量 $line 并且返回 $line 的值,因此 while 语句的条件表达式为真,你也许想知道,当遇到空白行时,Perl 是不是返回假并且过早地退出循环。答案是否定的,如果你还记得我们先前学过的内容,那么理由是非常明显的。读行操作符在字符串最后并不去掉新行符,因此空白行的值是“\n”。并且我们知道“\n”不是假值。因此条件表达式为真,并且循环将继续。
另一方面,当我们最后达到文件结束的时候,读行操作符将返回未定义值,未定义值总是被解释为假。并且循环结束,正好是我们希望结束的时候。在 Perl 中不需要明确地测试 eof 的返回值,因为输入操作符被设计成可以在条件环境下很好的工作。
实际上,几乎所有东西都设计成在条件环境下能很好工作,如果在一个标量环境中使用一个数组,就会返回数组的长度。因此你使用下面的代码处理命令行参数:
while (@ARGV) {
process(shift @ARGV);
}每次循环,shift 操作符都从参数数组中删除一个元素(同时返回这个元素),当数组 @ARGV 用完时循环自动退出,这时候数组长度变为 0,而在 Perl 中认为 0 为假。所以数组本身已经变为“假”。(注:这是 Perl 程序员的看法,因此我们没有比较拿 0 和 0 比较以证实它是否为假。但是其他语言却强迫你这么做,如果你不写 while(@ARGV = 0) 就不退出。这样做不论对你还是对计算机还是以后维护你的代码的人来说,都是效率低下的做法。)
1.6.3.2 for 语句
另外一个循环语句就是 for 循环。for 循环和 while 循环非常相似,但是看起来有很多不同之处。(C 语言程序员会觉得和 C 中的 for 循环非常相似。)
for ($sold = 0; $sold < 10000; $sold += $purchase) {
$available = 10000 - $sold;
print "$available tickets are available. How many would you like: ";
$purchase = <STDIN>;
chomp($purchase);
}for 循环在园括弧中有三个表达式:一个表达式初始化循环变量,一个对循环变量进行条件判断,还有一个表达式修改条件变量。当一个 for 循环开始时,设置初始状态并且检查条件判断,如果条件判断为真,就执行循环体。当循环体中的语句,修改表达式执行。并且再次检查条件判断,如果为真,循环体返回下一个值,如果条件判断值为真,循环体和修改表达式将一直执行。(注意只有中间的条件判断才求值,第一个和第三个表达式只是修改了变量的值,并将结果直接丢弃!)
1.6.3.3 foreach 语句
Perl 中最后一种循环语句就是 foreach 语句,它是用来针对一组标量中的每一个标量运行同一段程序,例如一个数组:
foreach $user (@users) {
if (-f "$home{$user}/.nexrc") {
print "$user is cool\&\.\.\. they use a perl-aware vi!\en";
}
}不同于 if 和 while 语句,foreach 的条件表达式时在列表环境中,而不是标量环境。因此表达式用于生成一个列表(即使列表中只有一个标量)。然后列表中的每个元素按顺序作为循环变量,同时循环体代码针对每个元素执行一次。注意循环变量直接指向元素本身,而不是它的一个拷贝,因此,修改循环变量,就是修改原始数组。
你将发现在 Perl 程序中,foreach 循环比 for 循环要多得多,这是因为 Perl 经常使用一些需要使用 foreach 遍历的各种列表。你经常会看到使用下面的代码来遍历散列的关键字:
foreach $key (sort keys %hash) {实际上“平均分”例子中的第 9 行也用到了它。1.6.3.4 跳出控制结构: next 和 last
next 和 last 操作符允许你在循环中改变程序执行的方向。你可能会经常遇到一些的特殊情况,碰到这种情况时你希望跳过它,或者想退出循环。比如当你处理 Unix 账号时,你也许希望跳过系统账号(比如 root 或 lp),next 操作符允许你将跳至本次循环的结束,开始下一个循环。而 last 操作符允许你跳至整个循环的结束,如同循环条件表达式为假时发生的情况一样。例如在下面例子中,你正在查找某个特殊账号,并且希望找到后立即退出循环,这时候,last 就非常有用了:
foreach $user (@users) {
if ($user eq "root" or $user eq "lp") {
next;
}
if ($user eq "special") {
print "Found the special account.\en";
# 做些处理
last;
}
}当在循环中做上标记,并且指定了希望退出的循环,next 和 last 就能退出多重循环。结合语句修饰词(我们稍后会谈到的条件表达式的另外一种形式),能写出非常具有可读性的退出循环代码(如果你认为英语是非常容易读懂的):
LINE: while ($line = <ARTICLE>) {
last LINE if $line eq "\en"; # 在第一个空白行处停止
next LINE if $line =~ /^#/; # 忽略注释行
# 你的东西放在这里
}你也许会说:稍等,在双斜杠内的 ^# 看起来并不象英语。没错,这就是包含了一个正则表达式的模式匹配(虽然这是一个很简单的正则表达式)。在下一节中,我们将讲述正则表达式。Perl 是最好的文本处理语言,而正则表达式是 Perl 文本处理的核心。
1.7 正则表达式
正则表达式(也可以表示为 regexes,regexps 或 Res)广泛使用在很多搜索程序里,比如: grep 和 findstr,文本处理程序如:sed 和 awk,和编辑器程序,如:vi 和 emacs。一个正则表达式就是一种方法,这种方法能够描述一组字符串,但不用列出所有的字符串。(注:一本关于正则表达式的概念的好书是 Jeffrey Friedl 的“Mastering Regular Expressions”(O'Reilly & Associates)
其它的一些计算机语言也提供正则表达式(其中的一些甚至宣扬“支持 Perl5 正则表达式”)但是没有一种能象 Perl 一样将正则表达式和语言结合成一体。正则表达式有几种使用方法,第一种,也是最常用的一种,就是确定一个字符串中是否匹配某个模式,因为在一个布尔环境中它们返回真或假。因此当看见 /foo/ 这样的语句出现在一个条件表达式中,我们就知道这是一个普通的模式匹配操作符:
if (/Windows 95/) { print "Time to upgrade?\n" }第二种方法,如果你能将一个模式在字符串中定位,你就可以用别的东西来替换它。因此当看见 s/foo/bar/ 这样的语句,我们就知道这表示将 foo 替换成 bar。我们叫这是替换操作符。同样,它根据是否替换成功返回真或假。但是一般我们需要的就是它的副作用:
s/Windows/Linux/;最后,模式不仅可以声明某地方是什么,同样也可以声明某地方不是什么。因此 split 操作符使用了一个正则表达式来声明哪些地方不能匹配。在 split 中,正则表达式定义了各个数据域之间定界的分隔符。在我们的“平均分”例子中,我们在第 5 和 12 行使用了两次 split,将字符串用空格分界以返回一列词。当然你可以用正则表达式给 split 指定任何分界符:
($good, $bad, $ugly) = split(/,/, "vi,emacs,teco");(Perl 中有很多修饰符可以让我们能轻松完成一些古怪的任务,例如在字符匹配中忽略大小写。我们将在下面的章节中讲述这些复杂的细节)
正则表达式最简单的应用就是匹配一个文字表达式。象上面的例子中,我们匹配单个的逗号。但如果你在一行中匹配多个字符,它们必须按顺序匹配。也就是模式将寻找你希望要的子串。下面例子要完成的任务是,我们想显示一个 html 文件中所有包含 HTTP 连接的行。我们假设我们是第一次接触 html,而且我们知道所有的这些连接都是有“http:”,因此我们写出下面的循环:
while ($line = <FILE>) {
if ($line =~ /http:/) {
print $line;
}
}在这里,= ~ 符号(模式绑定操作符)告诉 Perl 在 $line 中寻找匹配正则表达式“http:”,如果发现了该表达式,操作符返回真并且执行代码段(一个打印语句)。(注:非常类似于 Unix 命令 grep 'http:' file 做的事情,在 MS-DOS 里你可以用 find 命令,但是它不知道如何做更复杂的正则表达式。(不过,Windows NT 里名字错误的 findstr 程序知道正则表达式。))另外,如果你不是用 = ~ 操作符,Perl 会对缺省字符串进行操作。这就是你说“Eek,帮我找一下我的联系镜头!”,别人就会自动在你周围寻找,而不用你明确告诉他们。同样,Perl 也知道当你没有告诉它在那里寻找的时候,它会在一个缺省的地方寻找。这个缺省的字符串就是 $_ 这个特殊标量。实际上,$_ 并不是仅仅是模式匹配的缺省字符串。其它的一些操作符缺省也使用 $_ 变量。因此一个有经验的 Perl 程序员会将上个例子写成:while (<FILE>) {
print if /http:/;
}(这里我们又提到另外一个语句修饰词。阴险的小动物。)上边的例子十分简洁,但是如果我们想找出所有连接类型而不是只是 http 连接时怎么办?我们可以给出很多连接类型:象“http:”,“ftp:”,“mailto:”等等。我们可以使用下面的代码来完成,但是当我们需要加进一种新的连接类型时怎么办?
while (<FILE>) {
print if /http:/;
print if /ftp:/;
print if /mailto:/;
# 下一个是什么?
}因为正则表达式是一组字符串的抽象,我们可以只描述我们要找的东西:后面跟着一个冒号的一些字符。用正则表达式表示为 /[a-zA-Z]+:/,这里方括弧定义了一个字符表,a-z 和 A-Z 代表所有的字母字符(划线表示从开头的字符到结尾字符中间的所有字母字符)。+ 是一个特殊字符,表示匹配“+ 前边内容一次或多次”。我们称之为量词,这是表示允许重复多少次的符号。(这里反斜杠不是正则表达式的一部分,但是它是模式匹配操作符的一部分。在这里,反斜杠与包含正则表达式的双引号起同样的作用)。因为字母字符这种类型经常要用到,因此 Perl 定义了下面一些简写的方式:
| 名字 | ASCII 定义 | 代码 |
| 空白 | [\t\n\r\f] | \s |
| 词 | [a-zA-Z_0-9] | \w |
| 数字 | [0-9] | \d |
注意这些简写都只匹配单个字符,比如一个 \w 匹配任何单个字符,而不是整个词。(还记得量词 + 吗?你可以使用 \w+ 来匹配一个词)。在 Perl 中,这些通配符的大写方式代表的意思和小写方式刚好相反。例如你可以使用 \D 表示左右非数字字符。
我们需要额外注意的是,\w 并不是总等于 [a-zA-Z0-9] (而且 \d 也不总等于 [0-9]),这是因为有些系统自定义了一些 ASCII 外的额外的字符,\w 就代表所有的这些字符。较新版本的 Perl 也支持 Unicode 字符和数字特性,并且根据这些特性来处理 Unicode 字符。(Perl 认为 \w 代表表意文字)。
还有一种非常特别的字符类型,用“.”来表示,这将匹配所有的字符。(注:除了通常它不会匹配一个新行之外。如果你有怀疑,点“.”在 grep (1) 里通常也不匹配新行。)例如, /a./将会匹配所有含有一个“a”并且“a”不是最后一个字符的字符串。因而它将匹配“at” 或“am”甚至“a!”,但不匹配“a”,因为没有别的字母在“a”的后面。同时因为它在字符串的任何地方进行匹配,所以它将匹配“oasis”和“camel”,但不匹配“sheba”。它将匹配 “caravan”中的第一个”a”。它能和第二个“a”匹配,但它在找到第一个合适的匹配后就停止了。查找方向是由左向右。
1.7.1 量词
刚才我们讨论的字符和字符类型都只能匹配单个字符,我们提到过你可以用 \w+ 来匹配多个 “文本”字符。这里 + 就是量词,当然还有其它一些。所有的量词都放在需要多重匹配的东西后边。
最普通的量词就是指定最少和最多的匹配次数。你可以将两个数字用花括弧括起来,并用逗号分开。例如,你想匹配北美地区的电话号码,使用 \d{7,11} 将匹配最少 7 位数字,但不会多于 11 位数字。如果在括弧中只有一个数字,这个数字就指定了最少和最多匹配次数,也就是指定了准确的匹配次数(其它没有使用量词的项我们可以认为使用了{1})。
如果你的花括弧中有最少次数和逗号但省略了最大次数,那么最大次数将被当作无限次数。也就是说,该正则表达式将最少匹配指定的最少次数,并尽可能多地匹配后面的字符串。例如 \d{7} 将匹配开始的七位号码(一个本地北美电话号码,或者一个较长电话号码的前七位),但是当你使用 \d{7,} 将会匹配任何电话号码,甚至一个国际长途号码(除非它少于七位数字)。你也可以使用这种表达式来表示“最多”这个含义,例如。{0,5} 表示至多五个任意字符。
一些特殊的最少和最多地经常会出现,因此 Perl 定义了一些特殊的运算符来表示他们。象我们看到过的 +,代表 {1,},意思为“最少一次”。还有 *,表示 {0,},表示“零次或多次”. ? 表示 {0,1},表示“零或一次”。
对于量词而言,你需要注意以下一些问题。首先,在缺省状态下,Perl 量词都是贪婪的,也就是他们将尽可能多地匹配一个字符串中最大数量的字符,例如,如果你使用 /\d+/ 来匹配字符串“1234567890”,那么正则表达式将匹配整个字符串。当你使用“.”时特别需要注意,例如有下边一个字符串:
larry:JYHtPh0./NJTU:100:10:Larry Wall:/home/larry:/bin/tcsh并且想用 /.+:/ 来匹配“larry:”,但是因为 + 是贪婪的,这个模式将匹配一直到 /home/larry: 为止。因为它尽可能多地匹配直到最后出现的一个冒号。有时候你可以使用反向的字符类来避免上边的情况,比如使用 /[^:]+:/,表示匹配一个或多个不是冒号的的字符(也是尽可能多),这样正则表达式匹配至第一个冒号。这里的 ^ 表示后边的字符表的反集。(注:抱歉,我们不是有意选用这个名词的,所以别骂我们。这也是 Unix 里写反字符表的习惯方式。)另外需要仔细观察的就是,正则表达式将尽早进行匹配。甚至在它变得贪婪以前。因为字符串扫描是从左向右的,这就意味着,模式将尽可能在左边得到匹配。尽管也许在后边也能得到匹配。(正则表达式也许贪婪,但不会错过满足条件的机会)。例如,假设你在使用替换命令(s///)处理缺省字符串(变量 $_),并且你希望删除中间的所有的 x。如果你说:
$_ = "fred xxxxxxx barney"; s/x*//;但是上面的代码并没有达到预想的目的,这是因为 x*(表示零次或多次“x”)在字符串的开始匹配了空字符串,因为空字符串具有零字符宽度,并且在 fred 的 f 字符前正好有一个空字串。(注:千万不要感觉不爽,即使是作者也经常惨遭毒手。)
还有一件你必须知道的事情,缺省时量词作用在它前面的单个字符上,因此 /bam{2}/ 将匹配 “bamm”而不是“bambam”。如果你要对多于一个字符使用量词,你需要使用圆括弧,因此为了匹配“bambam”需要使用 /(bam){2}/。
1.7.2 最小匹配
如果你在使用老版本的 Perl 并且你不想使用贪婪匹配,你必须使用相反的字符表(实际上,你还是在使用不同形式的贪婪匹配)。
在新版本 Perl 中,你可以强制进行非贪婪匹配。在量词后面加上一个问号来表示最小匹配。我们同样的用户名匹配就可以写成 /.*?:/。这里的 .*? 现在尽可能少地匹配字符,而不是尽可能多的匹配字符。所以它将停止在第一个冒号而不是最后一个。
1.7.3 把钉子敲牢
你无论什么时候匹配一个模式,正则表达式都尝试在每个地方进行匹配直到找到一个匹配为止。一个锚点允许你限制模式能在什么地方匹配。基本来说,锚点匹配一些“无形”的东西,这些东西依赖于周边的特殊环境。你可以称他们为规则,约束或断言。不管你怎么称呼它,它都试图匹配一些零宽度的东西,或成功或失败(失败仅仅意味着这个模式用这种特殊的方法不能匹配。如果还有其它方法可以试的话,该模式会继续用其它方法进行匹配。)
特殊符号 \b 匹配单词边界,就是位于单词字符(\w)和非单词字符(\W)之间的零宽度的地方。(字符串的开始和结尾也被认为是非单词字符)。例如: /\bFred\b/ 将会匹配 “The Great Fred”和“Fred the Great”中的 Fred,但不能匹配“Frederick the Great” ,因为在“Frederick”中的“d”后面没有跟着非单词字符。
同理,也有表示字符串开始和结尾的锚点,^ 如果放在模式中的第一个字符,将匹配字符串的开始。因此,模式 /^Fred/ 将匹配“Frederick the Great”中的“Fred”,但不配 “The Great Fred”中的”Fred”。相反,/Fred^/ 两者都不匹配。(实际上,它也没有什么意义。)美元符号($)类似于^,但是 $ 匹配字符串的结尾而不是开头。(注:这么说有点过于简单了,因为我们在这里假设你的字串不包含新行;^ 和 $ 实际上是用于行的开头和结尾,而不是用于字串的。我们将在第五章,模式匹配里通篇强调这一点(做我们做得到的强调)。)
现在你肯定已经理解下面的代码:
next LINE if $line =~ /^#/;这里我们想做的是“当遇到以 # 开头的行,则跳至 LINE 循环的下一次循环。”
早些时候,我们提到过 \d{7,11} 将匹配一个长度为 7 到 11 位的数字。但是严格来讲,这个语句并不十分正确:当你在一个真正的模式匹配操作符中使用它的时候,如 /\d{7,11}/,它并不排除在 11 位匹配的数字外的数字。因此你通常需要在量词两头使用锚点来获取你所要的东西。
1.7.4 反引用
我们曾经提到过可以用圆括弧来为量词包围一些字符。同样,你也可以使用圆括弧来记住匹配到的东西。正则表达式中的一对圆括弧使得这部分匹配到的东西将被记住以供以后使用。它不会改变匹配的方式,因此 /\d+/ 和 /(\d+)/ 仍然会尽可能多地匹配数字。但后边的写法能够把匹配到的数字保存到一个特殊变量中,以供以后反向引用。
如何反向引用保存下来的匹配部分决定于你在什么地方使用它,如果在同一个正则表达式中,你可以使用反斜杠加上一个整数。数字代表从左边开始计数左圆括弧的个数(从一开始计数)。例如,为了匹配 HTML中 的标记如“Bold”,你可以使用 /<(.*?)>.*?<\/\1>/。这样强制模式的两个部分都匹配同样的字符串。在此,这个字符串为“B”.
如果不在同一个正则表达式,例如在替换的置换部分中使用反引用,你可以使用 $ 后边跟一个整数。看起来是一个以数字命名的普通标量变量。因此,如果你想将一个字符串的前两个词互相调换,你可以使用下面的代码:
/(\S+)\s+(\S+)/$2 $1/上边代码的右边部分(第二第三个反斜杠之间)几乎就是一个双引号字符串,在这里可以代换变量。包括反向引用。这是一个强大的概念:代换(在有所控制的环境中)是 Perl 成为一种优秀的文本处理语言的重要原因之一。另外一个原因就是模式匹配,当然,正则表达式方便将所需要的东西分离出来,而代换可以方便将这些东西放回字符串。
1.8 列表处理
本章早些时候,我们提过 Perl 有两种主要的环境:标量环境(处理单个的事物)和列表环境(处理复数个事物)。我们描述过的很多传统操作符都是严格在标量环境下执行。他们总是有单数的参数(或者象双目操作符一样有一对单数的参数)并且产生一个单数的返回值。甚至在列表环境中亦如此。当你使用下面的代码:
@array = (1 + 2, 3 - 4, 5 * 6, 7 / 8);你知道右边的的列表中包含四个值,因为普通数学操作符总是产生标量,即使是在给一个数组赋值这样的列表环境中。
但是,有一些 Perl 操作符能根据不同的环境产生一个标量或列表环境。他们知道程序需要标量环境还是列表环境。但是你如何才能知道?下面是一些关键的概念,当你理解这些概念之后,你就能很容易地知道需要标量还是列表了。
首先,列表环境必须是周围的事物提供的,在上个例子中,列表赋值提供了列表环境。早些时候,我们看到过 foreach 循环也能提供列表环境。还有 print 操作符也能提供。但是你不必逐个学习他们。
如果你通读本书其余部分种不同的语法说明,你会看到一些操作符定义为使用 LIST 作为参数。这就是提供列表环境的操作符。在本书中,LIST 作为一种特殊的技术概念表示”提供列表环境的句法”。例如,你观察 sort,你可以总结为:
sort LIST这表示,sort 给它的参数提供了一个列表环境。
其次,在编译的时候(当 Perl 分析你的程序,并翻译成内部操作码的时候),任何使用 LIST 的操作符给 LIST 的每个语法元素提供了列表环境。因此,在编译的时候,每个顶层操作符和 LIST 中的每个元素都知道 Perl 假设它们使用自己知道的方法生成最好的列表。例如当你使用下面的代码:
sort @dudes, @chicks, other();那么 @dudes,@chicks,和 other() 都知道在编译的时候 Perl 假设它们都产生一个列表值而不是一个标量值。因此编译器产生反映上述内容的内部操作码。
其后,在运行时候(当内部执行码被实际解释的时候),每个 LIST 成员按顺序产生列表,然后将所有单独的列表连接在一起(这很重要),形成一个单独的列表。并且这个平面的一维列表最后由那些需要 LIST 的函数使用。因此如果 @dudes 包含(Fred, Barney),@chicks 包含 (Wilma, Betty),而 other() 函数返回只有一个元素的列表 (Dino),那么 LIST 看起来就象下面一样:
(Fred,Barney,Wilma,Betty,Dino)sort 返回的 LIST:
(Barney,Betty,Dino,Fred,Wilma)一些操作符产生列表(如 keys),而一些操作符使用列表(如 print),还有其它一些操作符将列表串进其它的列表(如 sort)。最后的这类操作符可以认为是筛选器。同 shell 不一样,数据流是从右到左,因为列表操作符从右开始操作参数,你可以在一行中堆叠几个列表操作符:
print reverse sort map {lc} keys %hash;这行代码获取 %hash 的关键字并将它们返回给 map 函数,map 函数使用 lc 将所有的关键字转换成小写,并将处理后的结果传给 sort 函数进行排序,然后再传给 reverse 函数, reverse 函数将列表元素颠倒顺序后,传给print函数打印出来。正如你看到的一样,使用 Perl 描述比使用英语要简单的多。
在列表处理方面还有很多方法可以写出很多更自然的代码。在这里我们无法列举所有方法。但是作为一个例子,让我们回到正则表达式,我们曾经谈到在标量中使用一个模式来看是否匹配。但是如果你在一个列表环境中使用模式,它将做一些其它的事情:它将获得所有的反引用作为一个列表。假设你在一个日志文件或邮箱中搜索。并且希望分析一些包含象“12:59:59 am” 这样形式时间的字符串,你可以使用下面的写法:
($hour, $min, $sec, $ampm) = /(\ed+):(\ed+):(\ed+) *(\ew+)/;这是一种同时设置多个变量的简便方法,但是你也可以简单的写:
@hmsa = /(\ed+):(\ed+):(\ed+) *(\ew+)/;这里将所有四个值放进了一个数组。奇秒的,通过从 Perl 表达式能力中分离正则表达式的能力,列表环境增加了语言的能力。有些人可能不同意,但是 Perl 除了是一种斜交语言外, 它还的确是一种正交语言.
1.9 你不知道但不伤害你的东西(很多)
最后,请允许我们再次回顾 Perl 是一种自然语言的概念。自然语言允许使用者有不同的技巧级别,使用语言不同的子集,并且边学边用。通常在知道语言的全部内容之前,他们就可以很好地运用语言。你不知道 Perl 的所有内容,正象你不知道英语的所有内容一样。但这在 Perl 文化中是明确支持的。即使我们还没有告诉如何写自己的子过程也这样,但是你能够使用 Perl 来完成你的工作。我们还没有开始解释如何来看待 Perl 是一种系统管理语言,或者一种原型语言,或者一们网络语言或面向对象的语言,我们可以写一整章关于这些方面的内容(我们已经写了)。但是最后,你必须建立起你对 Perl 的看法.就象画家自己造成创造力的痛苦一样。我们能教你我们怎么画,但是我们不能教你该化什么。并且可能有不同的方法去做同一件事。
- Set MYTITLE = Perl 编程:概述
- Perl>PerlProgramming3>AnOverviewofPerl
导入论坛 引用链接 收藏 分享给好友 推荐到圈子 管理 举报
TAG:
标题搜索
日历
|
|||||||||
| 日 | 一 | 二 | 三 | 四 | 五 | 六 | |||
| 1 | 2 | 3 | 4 | 5 | |||||
| 6 | 7 | 8 | 9 | 10 | 11 | 12 | |||
| 13 | 14 | 15 | 16 | 17 | 18 | 19 | |||
| 20 | 21 | 22 | 23 | 24 | 25 | 26 | |||
| 27 | 28 | 29 | 30 | 31 | |||||
数据统计
- 访问量: 2769
- 日志数: 555
- 建立时间: 2008-01-07
- 更新时间: 2008-06-24

