范围
在Perl中,可以使用两点运算符..
或三点运算符...
表示一个范围。在列表上下文中两者等价,在标量上下文中两者不等价。
列表上下文中的范围
在列表上下文中,对于范围A..B
来说,它返回从A到B中间所有的值,且包含边界的A和B,每一个值都是前一个值自增(即++
运算符)之后的结果。如果左边的A值大于右边的B值,将表示空范围。
例如3..6
表示3、4、5、6共四个数,'a'..'d'
表示a、b、c、d共四个字母。
范围常用来为列表提供数据。例如:
my @arr1 = 1..3; # 1 2 3
my @arr2 = ('A'..'Z'); # 所有大写字母
my @arr3 = ('a'..'z'); # 所有小写字母
my @arr4 = ('a'..'z','A'..'Z'); # 所有大小写字母
my @arr5 = (0,3..5,7,10..20); # 离散数据:0 3 4 5 7 10到20
my @arr6 = ('01'..'31'); # 两位数日期
my @arr7 = ('01'..'12'); # 两位数月份
通过范围来指定循环执行次数变得非常简单:
# 循环10次
for(1..10){
say $_;
}
范围也常用于标量上下文。在标量上下文中,范围表示一个布尔值,Perl将这种情况下的操作符称为flip..flop
,flip就像合上开关,flop就像打开开关。
标量上下文中的范围,表示的含义是:
A..B
:从表达式A返回布尔真开始,到表达式B返回布尔真结束,评估表达式A之后会立即评估表达式BA...B
:从表达式A返回布尔真开始,到表达式B返回布尔真结束,评估表达式A之后不会立即评估表达式B,而是下一次再评估B
无论是哪种方式,在左表达式开始为真之前,不进入范围,此时不会评估表达式B;在左表达式开始为真后,进入范围,在右表达式为真结束范围之前,不会再继续评估左表达式。简单来说,对于A..B
,在A为真之前,不会执行B,在A为真之后、B为真之前,不会再执行A。
例如:
my $i = 0;
# 从0开始,左表达式为真,开始进入范围
# 到5结束,此时右表达式为真,结束范围
# 范围不要放在for、foreach里,它们是列表上下文
while(($i==0)..($i==5)){
say $i; # 输出:0 1 2 3 4 5
$i++;
}
注意,最好不要让右表达式处在左表达式所表示的范围中。例如,下面将是一个无限循环:
my $i = 0;
# 无限循环,从0到5是正常的范围,
# 从6开始,左表达式再次为真,但右表达式一直为假
while(($i>=0)..($i==5)){
say $i;
$i++;
}
如果进入范围时,左表达式和右表达式都为真,对于A..B
,将立即结束范围。如果不想在进入范围时评估B,使用A...B
。
例如:
my $i = 0;
# i为0时,左右表达式都为真,立即终止范围
# 因此只输出0
while(($i==0)..($i>=0)){
say $i;
$i++;
}
$i = 0;
# 变量i为0时,左表达式为真,开始进入范围,
# 此时不评估右表达式,第二轮循环才评估右表达式
# 因此输出0和1
while(($i==0)...($i>=0)){
say $i;
$i++;
}
很多时候,标量上下文中的范围flip..flop
的左右表达式都是字面量而不是完整的表达式。如果flip或flop为数值,则该数值将和所读取内容的行号进行比较,即取行号范围,此时和sed、awk的..
效果一致,如果flip或flop为正则表达式,则该正则表达式将与所读取含的内容$_
做匹配。这种用法在Perl一行式命令中非常方便。
if(100..200){print;} # 输出第100行到第200行
next if (1.../^$/); # 跳过文件开头的所有空行
next if(/^$/..eof()); # 忽略从空行开始的所有行