列表和数组的概念与性质
列表是Perl中的第二种数据结构,是一组标量的有序集合。数组是存储列表的容器,是一种可以存储列表中的多个标量值的变量,即数组变量。数组变量同标量变量,其中的每个值均可不断改变,但数组的名称是不变的。列表不一定存放在数组中,但每一个数组肯定包含一个或多个列表。
列表和数组中的每一个元素都是一个独立的标量,其值都互不相干,可以为任意类型的标量值。但最常见的是相同类型的一组数据组成的一个列表或数组,如一串数字、一串单词等。数组和列表可以包含任意多个元素,从没有任何元素的空列表、空数组到包含无限多个元素的列表或数组均可。
列表和数组具有有序性,其中元素的排列顺序是固定且分先后顺序的,这是列表和数组的重要特征。每一个元素都有一个索引值作为索引。索引值为整数,从0开始递增,每次加1,即列表或数组中第一个元素的索引值为0,第二个元素为1,以此类推。
列表直接量
列表直接量即一个列表在源代码中的书写方法。在一对圆括号内用逗号隔开一串数据,即一串列表元素,就构成了一个列表直接量。列表元素可以是标量,也可以是表达式,这些表达式在列表每次被调用时都会替换为其当前的计算结果。例:
(1,2,3,4,5) ($n,$m,$i) ('a','b','c') #各种列表
($n+$m) #只含有一个元素的列表
列表元素之间用逗号隔开,但两个列表元素之间不一定只能有逗号,在不出现歧义的前提下,还可以向两个列表元素之间加入其他空白符,常见的可以在列表元素之间用换行符隔开。例:
(
'a',
'b',
'c',
); #只要能划清各个列表元素界限,可以在逗号后加入换行符或其他。列表的最后一个逗号对列表来说无关紧要
qw简写
上文中出现了字符串列表('a','b','c'),在书写时引号很多,为避免这种键入大量字符串引号的麻烦,Perl提供了qw简写来省略其内部的字符串引号。qw表示“加上引号的单词”,在qw内,字符串不需要用引号引起,且会被当作单引号圈引的字符来处理。所以在qw内,不能使用变量内插,反斜线转义等功能。
qw简写由关键字qw和一对定界符组成。定界符可以选用任何标点符号,但应使定界符尽量不与其内部内容中含有的符号相冲突,如果遇到这种情况,可以使用反斜线转义表示符号本身。常用的定界符有斜线,小括号,花括号等。
在qw简写中,元素与元素之间以空白符隔开。空白符主要指空格,换行符(\n)和制表符(\t)等。在形成的列表中,所有的空白符会被忽略,只留下列表元素。例:
qw/a b c/
qw/
a
b
c
/ #分别以空格和换行符隔开的列表元素
范围操作符
另一种在列表中常用的简写,即范围操作符“..”,其可以生成一个递增的数字列表。范围操作符从左边的数字开始,到右边的数字结束,生成一连串每次加1的连续的整数。所以范围操作符右边的操作数应大于左边的(如相等则无意义),否则会生成空列表。范围操作符会先对两边的操作数取整,再进行计算。例:
(1..5) #相当于(1,2,3,4,5)
列表的赋值
类似于标量变量的赋值,列表元素也可以被赋值,即使用一个含有多个值的列表赋值给需要被赋值的列表。例:
($n,$m,$i)=(1..3);
上例中,先对右边的范围操作符进行展开,生成列表(1,2,3)。而后将这个列表中的元素1,2,3分别各自赋值给$n,$m,$i,这就相当于做了三次独立的赋值操作。
列表的赋值常用于交换两个变量的值。例:
($n,$m)=($m,$n); #$m赋值给$n,$n赋值给$m
在赋值时,如果右边的列表元素个数大于被赋值的列表元素个数,则后面多出的元素会被忽略。如果右边列表元素的个数小于被赋值的列表元素个数,则左边没有被赋值的列表元素就是undef。
数组与数组的赋值
数组是存放列表的容器,其本质上为一种变量。数组的赋值由列表完成,最常见的做法是将一个列表整体赋值给一个数组
Perl中,使用$符号来访问单个数据,相对应的,使用@符号来访问“所有的,全部的”数据。当需要访问整个数组时,就如同要访问一个标量变量时,使用@加上数组名即可。数组名也由标识符组成,即由字母或下划线开头,后接数字、字母或下划线。数组变量有其独立的名字空间(后续的很多名字空间都有这个性质),即数组变量名可以和标量变量名一模一样且在语法上没有错误,但不推荐这种容易引起歧义的代码写法。例:
@n=1..10; #将整个列表赋值给一个数组
上例中省略了列表括号,这里体现了Perl代码的一个省略原则:如果有无括号不会对代码产生歧义,则括号可省略。
整个数组也可放在赋值式的右边作为被赋值的内容,此时数组会先展开形成一个列表再进行赋值。例:
@m=@n; #复制数组,@n先展开为一个列表再赋值给@m
@m=(@n,1,2,3); #@n先展开为列表,作为赋值列表的一部分参与赋值
访问数组元素
同调用标量变量,调用数组变量时也使用$符号开头,表示访问单个数据。$后接数组名称,再后接下标数字,即由一对方括号围住的一个索引值,来调用一个数组元素。这样的表达式就称为下标表达式。例:
$n[3] #调用数组n中索引值为3(即第四个)的元素
数组的赋值也可以通过调用数组元素来完成。数组会根据被赋值元素的需要自动扩大。中间所有未赋值的元素均为undef。例:
$n[3]=3; #将数组n中索引值为3的元素赋值为3
($n[0],$n[1],$n[2])=1..3; #将数组n中前三个元素用列表进行赋值
$n[100]=1; #数组n将自动扩大到101个元素
下标不一定要是具体的数字,也可以是一个得到数字的表达式,方括号中的内容同双引号圈引的字符串一样处理,常见的下标是一个标量变量。例:
$n[$_] #调用数组n中索引值为$_的元素
下标索引值不一定要是正数,也可以是负数,即负数数组索引值。表示从数组末尾倒数第n个元素。例:
$n[-1] #数组n中最后一个元素
可以用$#加上数组名来表示该数组最后一个元素的索引值。例:
$n[$#n] #同$n[-1]
0..$#n #由0到数组n最后一个元素的索引值组成的一串数字列表
使用赋值给数组
不仅可以录入一个数据并赋值给一个标量变量,其也可以录入多行数据,将每一行的数据依次作为一个列表元素,并在录入结束后返回一个列表,此列表就可以赋值给数组。同标量变量赋值式,数组赋值式也可以与chomp操作符连用。在录入完成后,按下ctrl+Z,再按下回车即可结束录入。例:
chomp(@n=); #录入多行数据,去掉每一行数据末尾的换行符,录入完成后将所有录入的数据形成一个列表返回,并赋值给数组
数组内插
同标量变量内插,数组变量和整个数组均可以内插到双引号圈引的字符串中。其中,数组变量内插和标量变量内插的用法和效果一致。而内插整个数组时,会自动在每两个数组元素之间加上空格来分隔。例:
@n=1..10; #定义数组n
print"$n[1]\n@n"; #在双引号圈引的字符串中内插数组变量和整个数组,内插的@n中每个元素会自动以空格隔开
pop、push、shift和unshift操作符
Perl中有许多的操作符和函数可以对数组进行处理。当需要在数组的开头或结尾添加或删除数组元素的时候,就可以用到标题中的四个操作符。
pop和shift操作符分别可以取出目标数组的第一个和最后一个元素并将其作为返回值返回,其参数为目标数组。如果单独使用操作符而没有接受操作符返回值的变量,则操作符的作用就是直接删除这个元素。例:
@n=1..10;
pop@n; #此时@n为1..9
shift@n; #此时@n为2..9
$i=shift@n; #此时$i为2,@n为3..9
相对应的,push和unshift操作符的作用为在目标数组的尾部或开头压入一些元素,使其成为目标数组的新元素。其参数有两个,分别为目标数组和需要压入的新元素,可以为标量,也可以为数组或列表。例:
@n=1..10;
unshift@n,1,2,3; #在@n开头压入列表(1,2,3),省略了列表括号
push@n,@n; #在@n结尾再压入一个@n展开形成的列表
注意,上述四个操作符都必须对数组进行操作,对列表直接量进行操作是无意义的。
splice操作符
上文中的四个操作符都是对数组的开头和结尾进行操作的,而splice操作符可以对数组的任意位置,任意多个元素进行操作。其可以接受2至4个参数,分别为目标数组,起始索引值,长度和进行替换的内容,后两个参数是可选的。
splice操作符可以对目标数组根据给定的起始索引值和长度取出一串数组元素,并将取出的内容以一个列表返回。如果省略第三参数,则长度为从起点一直到数组末尾。例:
@m=splice@n,1; #从@n索引值为1的元素开始取出数组元素,由于无第三参数,所以会从起点一直取到数组末尾,并将取出的列表赋值
@i=splice@n,1,2; #从@n索引值为1的元素开始取出两个数组元素,并将由这两个元素构成的列表赋值
splice操作符的第四参数中的内容用于替换原数组中被取走的元素。替换进的数组元素个数不需要与取走的一致,数组会自动进行变化。当替换入新元素时,第一个新元素会被插入到第二参数,即起点索引值的位置,然后向后顺移其他替换进的元素和原来未被取走的元素。而如果将操作符的第三参数设为0,即取走元素的长度为0,就可以不取走原数组中的任何元素而直接在设定的起点处添加新的数组元素。例:
@n=1..5;
splice@n,2,2,2; #此时@n为(1,2,2)
splice@n,1,0,1; #在索引值1处添加新值,并向后顺移原来的元素,此时@n为(1,1,2,2)
foreach控制结构与默认变量$_
foreach控制结构与while一样,属于Perl五大循环块之一。其可以对整个数组或列表进行逐项遍历,依次进行循环。foreach控制结构由关键词foreach、控制变量、目标数组或列表和代码块组成。foreach可以简写为for,其他语句均不变,详见“其他控制结构”。foreach的用法如下:
@a=1..5;
for$n(@a){ #关键词+控制变量+目标数组
$n+=1; #对控制变量进行操作的代码块
} #结束后,@a变为了(2,3,4,5,6)
在上例中,每一次循环开始时,控制变量都会成为数组中某个元素的临时别名。第一次循环开始时,控制变量$n会成为数组中第一个元素$a[0]的临时别名,此时的$n就是$a[0]。所以在接下来的代码块中对$n进行操作,就会直接修改$a[0]的值,上例中会使得$a[0]的值加一。第一次循环结束后,$n又会成为$a[1]的临时别名,然后执行代码块。就这样不断进行循环,直到最后一个数组元素循环完毕,最终实现对整个数组或列表的依次遍历。
foreach结构中的控制变量仅在循环进行期间有效,如果控制变量先前有定义,那么当循环开始前其值会被保存,当循环结束后,控制变量仍会回到原先定义的值。
如果在foreach结构中省略控制变量,那么Perl就会使用其默认变量“$_”作为控制变量。这个变量十分重要,也十分常用。不仅是foreach,在许多其他情况下,如果代码中未指定使用哪个变量时,Perl都会使用默认变量$_。最常见的如print,如果未指定输出内容,那么print就会默认输出$_中的内容。例:
for(1..10){print} #没有控制变量时默认使用$_作控制变量,没有输出内容时默认输出$_中的值
上例中还用到了另一种省略,即代码块的最后一个分隔用分号可以省略。但这种省略一般只用于代码块内容简单到只有一行时才使用,且当代码块只有一行时,习惯上不使代码块换行缩进,而是将整个代码块写成一行,这样看上去更加紧凑。
列表排序初步
由于列表(也可以是数组展开得到的列表)元素的有序性,我们可以对其按一定的规则进行排序。
reverse函数以一个列表为参数,返回该列表的倒序列表,此函数只会生成返回值而不会将列表真正倒序。reverse函数与“..”连用是生成一个递减的数字串列表的最常用方法。例:
@n=reverse@n; #倒序@n展开成的列表,然后再赋值回@n
for(reverse 1..10){print} #返回倒序后的1..10列表给foreach循环
sort函数可以以一个列表作为参数,并根据ASCII码对每个列表元素的值进行升序排列,然后返回排序后的列表。例:
@n=sort@n; #对@n展开得到的列表进行排序,再将排序好的列表赋值回@n
上下文
上下文的概念是Perl中非常重要的一个概念,涉及到此概念的地方极其广泛,对上下文的合理、灵活地使用也非常重要。对上下文的简单理解即一个表达式会根据其周围的环境,即周围上下文的需要从而返回不同类型的值。例:
‘2’ * 2 #乘号需要的是数字,则字符串‘2’转换为数字2
2 x 2 #重复操作符左边为字符串,所以2转换为‘2’
上例便体现了Perl中操作符对上下文的决定作用。事实上,很多因素都能决定到底选用哪一种上下文。
在Perl中有很多种上下文类型,最常见的是标量上下文、列表上下文和空上下文。
在赋值式中,上下文主要决定于赋值式左边的内容,即决定于被赋值的事物需要怎样的类型。如果赋值等号左边是一个标量变量,即现在需要一个标量来赋值,那么此时就是标量上下文,而如果左边是数组或列表,那么此时就是列表上下文。也可以通过符号来判定,即$一般代表标量上下文,而@一般代表列表上下文。
在操作符或函数表达式中,上下文主要决定于参数需要的数据类型。如果参数需要的是标量,那么此时就是标量上下文,而如果参数需要的是列表,那么此时就是列表上下文。常见的上下文举例如下:
$n=sth; #标量上下文
@n=sth; #列表上下文
$n[2]=sth; #标量上下文
if(sth)、while(sth) #标量(布尔)上下文
($n)=sth; #列表上下文
push@n,sth; #列表上下文
for(sth) #列表上下文
reverse sth #列表上下文
上下文不能仅仅用一些规则来判定,更多的体现在实际的用法上。
如果在标量上下文中出现了列表或数组,最常见的如将一个数组赋值给标量变量,或者在x操作符右边使用数组等,此时数组返回的就不是由数组展开形成的列表,而是该数组中元素的个数,这是标量上下文中的一种非常重要的用法。例:
@a=1..10;
$n=@a; #赋值式为标量上下文,所以@a返回的是数组元素个数10
$m='m'x@a; #x操作符右边为标量上下文,所以@a返回10
而如果在列表上下文中出现了标量,则这个标量会自动转化为一个只含这个标量的列表。例:
@n=1; #1转化为(1),并赋值给@n
如果需要在列表上下文中强制切换到标量上下文,可以使用伪函数scalar,其可以将其后的内容强制切换到标量上下文。例:
print scalar@n; #强制将@n切入标量上下文,从而返回数组元素个数
樱雨楼
完成于2016.1.3
最后修改于2016.1.25