Perl 语言学习笔记 (二)

来源:互联网 发布:软件系统故障处理要求 编辑:程序博客网 时间:2024/06/05 02:54
这一部分主要是数组和列表部分。首先建议一种使用utf8编码的简便写法(因为Perl只能处理ASCII编码以及utf8编码,所以了解utf-8的详细用法是必须的): 
use utf8;binmode(STDIN, ':encoding(utf8)');binmode(STDOUT, ':encoding(utf8)');binmode(STDERR, ':encoding(utf8)');
这一部分写在脚本开头即可,表示utf8-flag为on的状态(对于Perl中utf8使用的更详细解释,请参看博文http://hi.baidu.com/acquiesce/item/4bd0a0f674d47815ce9f32ca)。没有必要对输入、输出以及错误报告都使用utf8编码模式,视程序的具体情况使用相应的代码就可以,在程序中写太多的无意义的代码是一件非常糟糕的事情。
废话讲完,开始更新笔记。
--------------------------------------------------------------
1. 列表指的是数据,数组指的是变量。列表的值不一定要放在数组里,但每个数组变量都一定包含一个列表(即便是不含任何元素的空列表)。
2. 数组元素的索引是从0开始递增,每次加1。不过,需要注意的是,Perl中的数组元素索引还允许使用负数(但是负数有特殊意义,与一般的正数索引值不同,见下文)。唯一的要求是,索引数字必须是整数(小数也不会报错,而是自动取整)。如果下标超过数组的范围,则对应的值是undef。数组和列表可以不包含任何元素,也可以包含任意多个元素(只要内存足够。这句话其实不够严谨,事实上最大的数组索引是有符号整数的最大取值,但是实际中不可能用到这么大的数字)。
3. 数组的名字空间与标量的名字空间是完全分开的,比如$fred[2]与$fred没有任何关系,这两个名称可以同时出现在同一段代码中。
对超出原数组尾端的新元素进行赋值,数组会根据需要自动扩大。譬如,原数组是$array[3],现在给$array[90]赋值,则数组会自动扩充到$array[90],这其中未明确赋值的元素均为undef。自动扩充只适用于正数索引值。
4. 数组$array的最后一个元素的索引值可以表示为$#array,这种写法与C Shell的表达相同,需要注意的是因为索引的开始值是0所以$#array这个数字比数组元素个数少1。另外一种方法是使用负数索引值来表示从原数组的末尾到开头的倒序读取,比如$array[-1]就表示$array数组的最后一个元素,$array[-2]表示倒数第二个元素。如果负数索引值超出了原数组的长度,则返回的值是undef。推荐使用负数索引值而不是$#array。负数索引值只有倒序读写已有数组元素的功能,不具有自动扩充数组范围的作用,这一点与正数索引值不同。
5. 列表直接量可以由圆括号内用逗号隔开的一串数据表示,这些数据被称为列表元素。例如("fred", 4.5)含有两个列表元素。如果要表示1到100之间的所有数字,可以使用(1..100)这种写法,两个连续的点号是范围操作符,表示从左边的数字每次增加1直到右边的所有数字,左右两个数字均为整数(小数会自动取整)。需要注意的是,这种形式是单方向的,左边数字必须小于右边数字,否则结果会是空列表。
6. 如果要建立一个字符串列表,那么使用qw简写可以省去键入很多引号的麻烦。例如,("fred", "barney", "betty", "wilma", "dino")等价于qw(fred barney betty wilma dino)。需要注意的是,qw()构建的列表中,字符串都被当做单引号内的字符串来处理,所以变量以及大多数转义都不适用,空白符(如空格、制表符以及换行符)会被抛弃。Perl允许的定界符很多,除了各种括号(包括<...>)之外,甚至一前一后相同的两个标点(比如!....!。但是括号必须是左右一对,不能都用左边或者都用右边)也可以用作定界符(也就是说qw!...!也是可以的)。如果当前作为定界符的标点出现在了某个元素中,那么需要更换其他定界符或者对元素中的标点换用转义表达,比如qw!google ask yahoo\! msn!的四个元素分别为google、ask、yahoo!以及msn。无论定界符是什么符号,元素内的反斜线都需要使用转义表达,即两个连续的反斜线\\表示转义的反斜线\,比如qw(This as a \\ real backslash)这种写法。推荐使用花括号{}作为定界符。
7. 列表的赋值操作例如($name, $age) = ("Hikigaya Hichimann", 17),相当于分别做了对$name和$age的独立的赋值操作。如果要对多个变量进行赋值,使用列表赋值可以免去很多冗余的单次赋值操作,让很多步赋值操作在一步操作中全部完成。同时,因为列表是在赋值运算开始之前建立的,所以列表可以用来交换变量的值。比如($a, $b)=($b, $a)可以交换变量$a与变量$b的值,无需再去建立中间变量,并且这对任意多个变量也是适用的。
8. 列表赋值操作总是以等号左侧的列表为基准。如果等号右边的列表的元素个数多于等号左边的列表的元素个数,那么多出来的右侧元素没有相应的被赋值对象,会被忽略;如果左侧元素个数多于右侧,那么左侧的多余元素没有相应的赋值,也不会进行赋值操作,其值保持原有的状态。
9. 在数组名之前加@表示引用整个数组。这可以用来快速给数组赋值。比如,@array=1..10表示从1到10的十个数字赋值给array数组,这个数组有十个元素。因为范围操作符生成的本来就是列表,所以没必要将其放在圆括号内。数组也可以作为列表元素赋值给新的数组,此时数组名会被展开成它所拥有的元素列表,譬如@a=(@b, "abcd", @c, @d, 12)这种方式,左端数组的序号按照右侧列表内的元素的顺序依次给定,如果右侧作为赋值的数组是空列表,则空列表会被忽略,显式指定的undef元素不会被忽略。例如,@name=("比企谷八幡", "雪之下雪乃", "比企谷小町"),@age=(17, 17, undef),@class=()是空列表,则@oregairu=(@name, @age, @class)等价于@oregairu=("比企谷八幡", "雪之下雪乃", "比企谷小町", 17, 17, undef)。需要注意的是,数组的元素只能包含标量,不能包含其他数组。作为列表元素的是数组的元素(@array表示array数组的全部元素)而不是数组本身。
10. 被赋值之前,数组变量的值是空列表,即()。把一个数组复制到另一个数组,这仍然是列表的赋值运算,所以使用@a=@b即可,即数组a的所有元素组成的列表被数组b的所有元素组成的列表赋值。
11. pop操作符用于取出数组的最后一个元素。对于数组来说,pop操作符会将数组的最后一个元素删除;但是pop操作符也类似于函数,它本身会返回取出的数组最后一个元素的值。pop(@a)与pop @a的写法都可以(建议带括号)。如果不使用新的变量存储pop返回的值而是直接使用pop(@a),那么就是数组a的最后一个元素被抛弃。如果数组本来就是空的,那么没有任何元素可以被pop操作符移除,此时pop操作符什么也不做,直接返回undef。
12. push操作符可以在原数组的最后新增加任意多个元素,写法为push(@array, New_Element),用不用括号都可以。新元素可以是一个,也可以是1..10这样的连续范围或者@b这样的由另一个数组的元素构成的列表,或者可以是新定义的某个列表。对于列表,可以使用push(@array, New_Element_1, New_Element_2, New_Element_3)这样的写法。
13. shift操作符与pop操作符类似,但是处理对象是数组的第一个元素。移除第一个元素之后,之后的元素自动向前移动一格。unshift操作符与push操作符类似,区别在于其操作对象也在数组的开头,作用是在数组开头(原来的第一个元素@array[0]之前)新增一个元素,原有的元素自动向后退一格。与pop类似,对于空的数组变量,shift会返回undef。
14. splice操作符可以从某个数组(该操作符的第一个参数)的指定元素(该操作符第二个参数)之后开始,取出指定个数(该操作符的第三个参数)的元素作为返回值(返回值是一个数组)并且在操作的位置替换新的列表元素(该操作符的第四个参数)。写法为@removed = splice @array, 1, 2, ("fred");这样。需要注意的是,第三个参数(取出元素的个数)与第四个参数(替换到原数组内的新元素)是可选的。如果没有第三个元素,则表示操作区域为第二个参数指定的位置到原数组的结尾;如果第三个元素为0,则表示不从原数组中删除原有的元素,而直接将第四个参数表示的新元素插入到第二个参数指定的位置。第四个参数所指定的列表中的新元素的个数不限。
15. 如果直接把数组的内容内插到双引号中,则这相当于按照索引顺序依次放置了数组的所有元素,并且相邻元素之间用空格隔开(事实上,分隔符由特殊变量$"的值指定,默认为空格,也可以改为其他)。需要注意的是,分隔符仅仅会被添加在相邻元素之间,数组的首尾都不会增加分隔符。如果不把数组内容放在引号里,则输出的是数组元素的个数。需要注意的是,如果想要在双引号中输出字符@,必须使用转义的写法\@,否则会被当做内插数组处理,当然使用单引号就不会出现这种问题(单引号内不存在数组内插,除了极少数写法之外,字符均表示其本身)。例如下面的代码中,while部分与print "@name"部分输出的结果相同: 
#! /usr/bin/perluse utf8;binmode(STDOUT, ':encoding(utf8)');@name = qw(比企谷八幡 雪之下雪乃 比企谷小町 由比滨结衣 川崎沙希);# 以while循环的方式依次输出数组元素$i = 0;while ($i < $#name + 1) {    print "$name[$i]\n";    $i += 1;}# 直接输出数组元素# 注意print之后的@name必须放在引号内,否则输出的是数组的元素个数$" = "\n";print "@name" . "\n";

16.  内插引用单个的数组元素的方式是$array[ID]。如果用于内插的不是数组元素而是一个普通变量,并且普通变量名之后有方括号,那么应该在变量名和方括号之间用某种方式隔开。例如,"$a[3]"会被判定为数组a的第三个元素,但是使用"$a"."[3]"或者"${a}[3]"或者"$a\[3]"就可以使其判定为变量$a之后跟着字符[3]。

17. foreach能够逐项遍历列表中的值。foreach $a (列表) {循环执行的操作}的作用是每次循环依次将列表中的一个元素赋值给$a,然后$a参与到循环操作中。这与while结构的循环有相同的作用,但是形式更加简洁且不需要计数变量。需要注意的是,列表必须放在括号内(所以对于数组而言必须是(@array)这样的形式)。循环结束后,控制变量的值(即foreach $a (列表) {循环执行的操作}中的$a)仍然是循环之前的状态,Perl会自动存储foreach循环的控制变量并在循环结束后还原。换一句话说,这里的循环控制变量叫什么名字都不重要,哪怕是它在循环之前就被使用在很重要的位置也完全没有关系。例如,对于15的例子(见上面的代码),以下的写法的输出结果与15例子中已有的while以及print直接输出两种方式的结果也是相同的:

# foreach输出数组元素foreach $name (@name) {print "$name\n";}

18. Perl最喜欢的默认变量是$_,所以foreach中也可以略去不写控制变量,变成foreach (列表) {循环执行的操作}这样的结构,此时控制变量为$_。$_除了名称比较特殊之外,与其他的标量变量几乎没什么区别。这样的话,上面的代码(17中的例子)可以简化为

# foreach输出数组元素foreach (@name) {print "$_\n";}

如果直接使用print;那么也会打印$_的值。当然,Perl中的默认变量不止这一个。

19. reverse操作符可以读取列表元素的值并且按照与原来的次序相反的顺序返回列表。需要注意的是,reverse不会修改传进来的参数,所以必须有列表承载这个返回值,否则reverse操作符无效。例如,@fred = reverse @fred;表示将数组@fred倒序后放回原数组。

20. sort操作符读取列表元素的值,并且按照unicode编码的顺序对元素进行排序,最后将排序后的列表作为返回值。与reverse操作符一样,sort操作符也不会修改传进来的参数,所以必须有列表承载这个返回值,否则sort操作符无效。

21. each操作符见66页。不推荐使用。

22. 标量上下文与列表上下文,即根据代码前后所表达的整体意义来判断某个标量名称或者列表名称的具体指代。比如之前例子中的@name可以指各元素组成的列表,也可以指元素的总体个数。比如

@family = splice @name, 0, 3;print ">>family member:\n";foreach (@family) {print "$_\n";}print "There are " . @family . " family members.\n";

第一行中的@family被赋值,是列表;而第六行中的@family则表示列表元素的总个数(假如使用$count = @family这样的赋值语句,那么很明显@family表示元素个数)。

23. 如果在标量上下文中使用产生列表的表达式,如sort之类,则结果并不一定是什么样子。对于reverse,假如使用$string = reverse qw(a b c d e)则会得到$string的值为"edcba",即此时先忽略原列表元素之间的所有间隔,将各元素依次连接成一个整体的字符串,然后reverse返回这个组合字符串的倒序结果。$a = sort qw(...)的结果则是undef。需要注意的是,$fred = something这是标量上下文,而($fred) = something则是列表上下文。

24. 如果在列表上下文中使用产生标量的表达式,则自动将此标量值作为列表的元素。例如@fred = 6*7等价于@fred = (42)。需要注意的是,清空一个数组务必使用@array = (),即将其重新赋值为空数组,而不是@array = undef。因为后者会把undef作为数组array的元素,此时数组并没有被清空。

25. 如果想要强制指定为标量上下文(譬如@a表示元素个数而非所有元素组成的列表),则使用伪函数scalar即可。譬如,下面这一行的作用与22例子中的最后一行的作用相同。

print "There are " , scalar @family , " family members.\n";
注意这一行中没有字符串的连接符号,而是逗号。此处scalar @family强制表示为标量上下文,即元素个数。

26. <STDIN>在标量上下文中会返回输入的一行;在列表上下文中,则会返回所有剩下的行,直到文件结尾为止,每一行都会成为列表中的元素。对于UNIX以及类UNIX系统,“文件结尾”的标记一般是Ctrl+D;对于DOS/Windows系统而言则是Ctrl+Z。当然也可以使用chomp(@lines = <STDIN>)这种方式去掉每一行之后的换行符。例如,对于文件input.txt,其内容为三行,即

比企谷八幡雪之下雪乃比企谷小町

另外有Perl脚本lines_input.pl,其内容为

#! /usr/bin/perluse utf8;binmode(STDIN, ':encoding(utf8)');binmode(STDOUT, ':encoding(utf8)');chomp(@lines = <STDIN>);print "There are " . @lines . " elements.\n";foreach (@lines) {print "$_\n";}

则在Linux终端中执行

./lines_input.pl < input.txt

得到的结果为

There are 3 elements.比企谷八幡雪之下雪乃比企谷小町

-------------------------------------------------------------

第三章习题简析

1. 分别以正序和倒序输出列表元素

#! /usr/bin/perluse utf8;binmode(STDIN, ':encoding(utf8)');binmode(STDOUT, ':encoding(utf8)');chomp(@lines = <STDIN>);# 正序输出列表元素print "There are " . @lines . " elements.\n";foreach (@lines) {print "$_\n";}# 倒序输出列表元素@reversed_lines = reverse @lines;print "elements after reverse\n";foreach (@reversed_lines) {print "$_\n";}

2. 按照输入的数字输出已知列表中相应位置的元素

#! /usr/bin/perl@name = qw(fred betty barney dino wilma pebbles bamm-bamm);chomp(@id = <STDIN>);while ($count < @id) {print "$name[$id[$count]-1]\n";$count += 1;}

3. 已知列表的元素排序,一行输出以及分行输出

#! /usr/bin/perlchomp(@lines = <STDIN>);@lines = sort @lines;foreach (@lines) {print "$_\t";}print "\n";foreach (@lines) {print "$_\n";}

0 0