Perl学习笔记03——列表和数组

列表和数组

1.列表指的是标量的有序集合,而数组则是存储列表的变量。更精确地说,列表指的是数据,而数组指的是变量。

2.数组或列表中的每个元素都有相应的整数作为索引,从0开始递增,每次加1

3.数组或列表每个元素都是独立不相关的标量值,所以列表或数组可能包含数字、字符串、undef值或是不同类型标量值的混合。

4.列表的值不一定要放在数组里,但每个数组变量都一定包含一个列表(即便是不含任何元素的空列表)。

访问数组中的元素

访问数组元素:

print $fred[0];

修改数组元素:

$fred[2] = "diddly";

任何求值能得到数字的表达式都可以用作下标,假如它不是整数,则会自动舍去小数,无论正负。

$number = 2.71828;
print $fred[$number - 1];  #结果和print $fred[1]相同

假如下标超出数组的末端,则对应的值将会是undef

特殊的数组索引($#rocks

如果对索引值超过数组尾端的元素进行赋值,数组将会根据需求自动扩大,增补元素默认取值为undef

$rocks[0] = 'bedrock';  #一个元素
$rocks[1] = 'slate';  #又一个元素
$rocks[99] = 'schist';  #现在有97个undef元素

最后一个元素的索引值

对数组rocks而言,最后一个元素的索引值是$#rocks,但这个数字比数组元素的个数少1,因为还有一个编号为0的元素。

$end = $#rocks;  #99,也就是最后一个元素的索引值
$number_of_rocks = $end + 1;  #100,数组元素的个数
print $rocks[$#rocks];  #访问最后一个元素

负数数组索引

注意:超出数组大小的负数索引值只会得到undef,而不会绕回到数组尾部。

print $rocks[-1];  #和$rocks[$#rocks]一样,但更简单
print $rocks[-100];  #得到'bedrock'
$rocks[-200] = 'crystal';  #致命错误!

列表直接量

列表直接量(也就是在程序代码中表示一列数据的写法),可以由小括号内用逗号隔开的一串数据表示,这些数据就称为列表元素。

(1,2,3)  #包含数字1、2、3这三个数字的列表
(1,2,3,)  #包含数字1、2、3(末尾的逗号会被忽略)
("fred",4.5)  #两个元素,"fred"和4.5
()  #空列表——零个元素

范围操作符(..

如果是某种规律的序列,不用逐个键入。范围操作符(..)可用于自动创建它两侧标量值之间的所有值。

(1..100)  #从1到100的整数序列
(1..5)  #等同于(1,2,3,4,5)
(1.7..5.7)  #同上,等同于(1,2,3,4,5),小数点部分去掉后取范围
(0,2..6,10,12)  #等同于(0,2,3,4,5,6,10,12)

NOTE

1.范围操作符只能从小到大依次累加

(5..1)  #得到的是空列表

2.列表中的元素可以不必都是常数,它们可以是表达式。

($m,17)
($m+$o, $p+$q)
($m..$n)
(0..$#rocks)  #rocks数组的下标序列

qw简写

qw简写可以快速输入,免除反复键入引号和逗号。

("fred","barney","betty","wilma","dino")
等同于
qw( fred barney betty wilma dino)

NOTE

1.qw构建的列表中,Perl都会将其当成单引号内的字符串来处理,所以,不能像双引号内的字符串一样使用 $fred其中的空白字符(如空格、制表符以及换行符)会被抛弃,然后剩下的就是列表中的元素

所以,上面列表可以写成这样:

qw( fred 
barney     betty 
wilma dino)

 2.因为qw算是一种引用的形式,所以不能将注释放在qw列表中

qw(
    fred  #abc,在这里加注释会被当成一个元素
    barney
    betty
    wilma
    dino
)

3.除了圆括号()Perl还允许用任何标点符号作为定界符,也可以是成对符号

qw!fred barney betty wilma dino!
qw/fred barney betty wilma dino/
qw#fred barney betty wilma dino#
qw{fred barney betty wilma dino}
qw[fred barney betty wilma dino]
qw<fred barney betty wilma dino>

注意:

1)如果需要在被圈引的字符串内使用定界符,可通过反斜线转义来引入这个字符。

qw! yahoo! google ask msn !  #将yahoo!作为一个元素引入

2)和单引号内的字符串一样,两个连续的反斜线表示一个实际的反斜线。

qw( This is a \ real backslash);

列表的赋值

▲列表值赋值到变量

($fred, $barney, $dino) = ("flintstone", "rubble", undef);

NOTE

1.如果变量个数>列表值的个数,多出来的值会被忽略掉

($fred, $barney) = qw< flintstone rubble slate granite >  #忽略掉末尾的两个元素

2.如果变量个数<列表值的个数,多出来的变量会被设成undef(或空列表,后面解释)

($wilma, $dino) = qw[flinstone];  #$dino的值为undef

▲交换两个变量的值

($fred, $barney) = ($barney, $fred);
($betty[0], $betty[1]) = ($betty[1], $betty[0]);

构建一个字符串数组

($rock[0], $rock[1], $rock[2], $rock[3]) = qw/talc mica feldspar quartz/;

引用整个数组(用@字符)

在赋值操作符两边都可以用

@rock = qw/bedrock slate lava/;
@tiny = ();  #空列表
@giant = 1..1e5;  #包含100000个元素的列表
@stuff = (@giant, undef, @giant);  #包含200001个元素的列表

注意:

1.

$dino = "granite";
@quarry = (@rock, "crushed rock", @tiny, $dino);

这个赋值运算将会把@quarry的值设成拥有5个元素的列表(bedrock, slate, lava, crushed rock, granite),因为@tiny贡献了0个元素给这个列表(注意,由于空列表里没有任何元素,也就不会有undef被赋值到列表中,如果需要undef,也可以显式写明)。

2.

列表中的数组名会被展开成(它所拥有的)元素列表,因为数组只能包含标量,不能包含其他数组,所以数组无法成为列表中的元素。

3.

就像标量变量的初始值是undef一样,新的或是空的数组的初始值是空列表,即()

poppush操作符

pop操作符:提取数组末尾的元素并将其作为返回值

@array = 5..9;
$fred = pop(@array);  # $fred为9,@array现在是(5,6,7,8)
$barney = pop @array;  # $barney为8,@array现在是(5,6,7)
pop @array;  # @array现在是(5,6),pop的返回值7被抛弃了

NOTE

1.最后一行是在“空上下文”中使用pop操作符。所谓空上下文,只不过是表示返回值无处可去的一种说辞。这其实也是pop操作符常见的一种用法,用来删除数组中最后一个元素。

2.如果数组是空的,pop什么也不做,直接返回undef

3.pop后面加不加括号都可以。

push操作符:添加一个(或一串)元素到数组尾端

@array = (5,6);
push (@array, 0);  # @array现在是(5,6,0)
push @array, 8;  # @array现在是(5,6,0,8)
push @array, 1..10  # @array得到了10个新元素
@others = qw/5 2 0 y f b/;
push @array, @others;  # @array又得到了6个新元素(共20个)

注意:

pushpop的第一个参数都必须是要操作的数组变量,对列表直接量进行压入(push)或弹出(pop)操作是没有意义的。(因为对列表直接量进行操作后,处理后的列表又怎么表示呢?)

shiftunshift操作符

shift操作符:提取数组开头的元素并将其作为返回值

@array = qw#dino fred barney#;
$m = shift(@array);  # $m变成"dino",@array现在是("fred", "barney")
$n = shift @arry;  # $n变成"fred",@array现在是("barney")
shift @array;  # 现在@array变空了
$o = shift @arry;  # $o变成undef,@array还是空的

unshift操作符:添加一个(或一串)元素到数组开头

unshift(@array, 5);  # @array现在仅包含只有一个元素的列表(5)
unshift @array, 4;  # @array现在是(4,5)
@others = 1..3;
unshift @array, @others;  # @array变成了(1,2,3,4,5)

splice操作符

push-popshift-unshift操作符都是针对数组首尾的元素操作的,splice操作符是针对数组中间的元素操作的。

splice含义:删除操作。

splice可接受4个参数,后两个参数可选

splice(参数1,参数2,参数3,参数4)

参数1要操作的数组

参数2要操作的一组元素的开始位置(索引值)

参数3(可选):指定要操作的元素长度(即元素个数)

参数4(可选):要替换的列表

接受两个参数:

@array = qw(pebbles dino fred barney betty);
@removed = splice @array, 2;  
#在原来的数组中删掉从fred(索引值为2)开始往后的元素
#@removed变成qw(fred barney betty)
#原先的@array变成qw(pebbles dino)

NOTE

1.splice返回值是什么? ——删除的元素列表

2."原地更新"吗? ——是

接受三个参数

3个参数作用是指定要操作的元素长度(注意不是结束位置),即元素个数,通过这个参数,可以删除数组中间的一个片段

@array = qw(pebbles dino fred barney betty);
@remoed = splice @array, 1, 2;
#删除dino和fred两个元素
# @remoed变成qw(dino fred)
# @array变成qw(pebbles barney betty)

接受四个参数

4个参数是要替换的列表,也就是可以补充新元素到原来的数组中,新加入列表的长度不一定要和拿走的元素片段长度一样。

@array = qw(pebbles dino fred barney betty);
@remoed = splice @array, 1, 2, qw(wilma);
#删除dino和fred
# @remoed变成qw(dino fred)
# @array变成qw(pebbles wilma barney betty)

特殊用法

实际上,添加元素列表并不需要预先删除某些元素,把表示长度的第三个参数设为0,既可不加删除地插入新列表。

@array = qw(pebbles dino fred barney betty);
@remoed = splice @array 1, 0, qw(wilma);
#什么元素都不删除
# @remoed变成()
# @array变成qw(pebbles wilma dino fred barney betty)

注意:

wilma出现在dino 之前的位置上,Perl从索引值1的地方插入新列表,然后顺移原来的元素

字符串中的数组内插

和标量一样,数组的内容同样可以被内插到双引号引起的字符串中。内插时,会在数组的各个元素之间自动添加分隔用的空格

@rocks = qw( flinstone slate rubble);
print "quartz @rocks limestone
";  #打印5个以空格隔开的单词

NOTE

1.数组被内插后,首尾都不会增添额外空格,若真的需要,可以自己手动添加

2.如果@为双引号内字符串的内容本身时,需引入反斜线转义@或直接用单引号来定义字符串

$email = "fred@bedrock.edu";  #错!这样会内插@bedrock这个数组
$email = "fred@bedrock.edu";  #正确
$email = 'fred@bedrock.edu';  #另一种写法,效果相同

3.内插数组中的某个元素时,会被替换成该元素的值。

@fred = qw(hello dolly);
$y = 2;
$x = "This is $fred[1]'s place";  #得到This is dolly's place
$x = "This is $fred[$y-1]'s place";  #效果同上

4.如果要在某个标量变量后面紧接着写左方括号,需要隔离,否则会被识别为数组引用。

@fred = qw(eating rocks is wrong);
$fred = 'right';
print "this is $fred[3]
";  #用到了$fred[3],打印"wrong"
print "this is ${fred}[3]
";  #打印right[3](用花括号避开歧义)
print "this is ${fred}"."[3]
";  #打印right[3](用分开的字符串)
print "this is $fred[3]
";  #打印right[3](用反斜线避开歧义)

foreach控制结构

foreach循环能逐项遍历列表中的值,依次提取使用:

foreach $rock(qw/ bedrock slate lava /){
    print "One rock is $rock.
";  #依次打印这三个单词    
}

NOTE

1.每次循环迭代时,控制变量$rock都会从列表中取得新值。

2.控制变量并不是列表元素的复制品,实际上,它就是列表元素本身。也就是说,假如在循环体中修改了控制变量的值,也就同时修改了列表元素。

@rocks = qw/ bedrock slate lava /;
foreach $rock(@rocks){
    $rock = "	$rock";  #在@rocks的每个元素前加上制表符
    $rock .= "
";  #同时在末尾加上换行符
}
print "The rocks are:
",@rocks;  #各自占一行,并使用缩排(注意这种写法,从第二个列表元素开始前面都带一个空格)

注意:

@rocks = qw(a b c d);
print @rocks;  #会得到abcd
print "@rocks";  #会得到a b c d

循环结束后,控制变量的值仍然是循环执行之前的值。Perl会自动存储foreach循环的控制变量并在循环结束后还原。

在循环执行期间,我们无法访问或改变已存储的值,所以当循环结束时,变量仍让保持循环前的值。

如果它之前从未被赋值,那就仍然是undef

也就是说,如果想把循环的控制变量取名为$rock的话,不必担心之前是否用过同名的变量。

$rock = 'shale';
@rocks = qw/ bedrock slate lava /;
foreach $rock(@rocks){
    ...
}
print "rock is still $rock
";  #打印'rock is still shale'

Perl的默认变量:$_

假如在foreach循环开头省略控制变量,Perl就会用它最喜欢用的默认变量$_

这个变量名除了名称比较特别以外,和其它标量变量几乎没什么差别。

foreach (1..10){  #默认会用$_作为控制变量
    print "I can count to $_!
";
}

Perl有许多默认变量,$_是最常用的一个,如果不加说明,Perl默认使用$_

$_ = "Yabba dabba doo
";
print;  #默认打印$_

reverse操作符

reverse操作符会读取列表的值(一般来自数组),并按相反次序返回新的列表。

@fred = 6..10;
@barney = reverse(@fred);  #返回10,9,8,7,6
@wilma = reverse 6..10;  #同上,但无需额外的数组
@fred = reverse @fred;  #倒序后保存到原来的数组

 注意:

reverse只是返回倒序后的列表,它不会修改给它的参数。加入返回值无处可去,那这种操作也就变得毫无意义。

reverse @fred;  #错误的用法,这不会使数组内容倒序
@fred = reverse @fred;  #正确用法

sort操作符

sort操作符会读取列表的值(一般来自数组),依次按字符的内部编码顺序对它们排序。

排序按照ASCII码的大小进行。

@rocks = qw/ bedrock slate rubble granite /;
@sorted = sort(@rocks);  #返回bedrock,granite,rubble,slate
@back = reverse sort @rocks;  #逆序,从 slate 到 bedrock 排列
@rocks = sort @rocks;  #将排序后的结果存到原数组@rocks
@numbers = sort 97..102;  #得到100,101,102,97,98,99

NOTE

1.按默认排序规则,任何以1开头的字符串会被排在以9开头的字符串之前。

2.排序操作和reverse操作一样,不会修改原始参数,只是返回新的列表所以要对数组排序,就必须将排序后的结果存回数组。

each操作符

Perl 5.12开始,已经可以针对数组使用each操作符了。在这之前each只能用于提取哈希的键-值对。

每次对数组调用each,会返回数组中下一个元素对应的两个值——数组索引与元素值:

require v5.12;
@rocks = qw/ bedrock slate rubble granite /;
while (($index, $value) = each @rocks){
    print "$index: $value
";
}

注意:

这里使用了require,因为使用use v5.12会默认启用“严格(strict)”模式。

如果不用each,就得根据索引从小到大遍历,然后借助索引取得元素值。

@rocks = qw/ bedrock slate rubble granite /;
foreach $index(0..$#rocks){
    print "$index: $rocks[$index]
";
}

标量上下文与列表上下文

同一个表达式出现在不同的地方时会有不同的意义。

所谓上下文,指的是如何使用表达式。

前文接触过很多针对数字和字符串的上下文操作,比如按照数字进行操作时得到的就是数字结果,按照字符串进行操作时返回的则是字符串结果。而且,起到决定性因素的是操作符,而不是被操作的各种变量或直接量

2*3  #得到数字6
2x3  #得到字符串222

Perl在解析表达式时,要么希望它返回一个标量,要么希望它返回一个列表。

表达式所在的位置,Perl期望得到什么,那就是该表达式的上下文。

42 + something  #这里的something必须是标量
sort something  #这里的something必须是列表

就算是同一个表达式名称,有时返回的是单个标量值,有时返回的却是列表,这取决于上下文。

Perl里,表达式总是按照需要的上下文返回对应的值。

NOTE

1.以数组的“名称”为例,在列表上下文中返回元素列表,在标量上下文中则返回数组的元素个数

@people = qw(fred barney betty);
@sorted = sort @people;  #列表上下文,得到 barney,betty,fred
$number = 42 + @people;  #标量上下文:42 + 3 得45

2.即使是普通的赋值操作符(对标量或列表赋值),也可以有不同的上下文。

@list = @people;  #得到3个人的姓名列表
$n = @people;  #得到人数3

3.不要立刻得出结论,认为列表在标量上下文中一定会得到元素的个数而非元素列表。许多能返回列表的表达式会有各种丰富有趣的行为

4.其实“产生列表”的表达式和“产生标量”的表达式之间并无不同,任何表达式都可以产生列表或标量,根据上下文而定。

在标量上下文中使用产生列表的表达式

sort在标量上下文中总是返回undef

@sorted = sort qw/ fred barney betty /;  #返回barney, betty, fred
$sorted = sort qw/ fred barney betty /;  #返回undef

reverse在标量上下文中返回逆序后的字符串(先将列表中所有的字符串全部连接在一起,再对结果中的每一个字符作逆序处理)

@sorted = reverse qw/ fred barney betty /;  #返回betty, barney, fred

$sorted = reverse qw/ fred barney betty /;  #返回yttebyenrabderf

一个典型的例子:

@array = qw( talc quartz jade );
if (@array){  #标量上下文中@array返回元素的个数3,表示真
    print "Hello";
}

在列表上下文中使用产生标量的表达式

在列表上下文中,如果表达式求值结果为标量值,则自动产生一个仅含此标量值的列表。

@fred = 6 * 7;  #得到仅有单个元素的列表(42)
@barney = "Hello".' '."world";   #得到仅有单个元素的列表('Hello world')

由于undef是标量值,所以把undef赋值给数组并不会清空数组,得到的还是一个数组,内含一个空值元素。

@wilma = undef;  #得到的是单个元素的列表,元素值为未定义的(undef)

要清空数组,直接赋予一个空列表:

@wilma = ( );  #这才是清空数组的正确方法

强制指定标量上下文

在列表上下文中强制引入标量上下文,使用伪函数scalar

它不是真正的函数,只是用于告诉Perl这里要切换到标量上下文。

@rocks = qw( talc quartz jade obsidian);
print "I have ", @rocks, " rocks!
";  #得到I have talcquartzjadeobsidian rocks!
print "I have ", scalar @rocks, " rocks!
";  #得到I have 4 rocks!

列表上下文中的<STDIN>

在标量上下文中,<STDIN>返回的是输入数据的下一行内容;

在列表上下文中,返回的则是所有剩下行的内容,一行一个元素,直到文件结尾。

如果输入数据来自键盘,如何发送结尾标记?

Unix或类似系统:Ctrl+D

DOS/WindowsCtrl+Z

输入完成后,每个元素都是一个以换行符结尾的字符串,因为这些换行符也是输入的内容。

chomp可以一次性去掉每个元素的换行符:

写法一:

@lines = <STDIN>;
chomp @lines;

写法二:

chomp(@lines = <STDIN>);

如果文件太大,行输入操作符会读取所有行并占用大量内存,所以这时应该选用更合理的方式。

原文地址:https://www.cnblogs.com/yd-yfb/p/13923434.html