前言
因为习惯了使用Perl
,所以一般写流程的时候就会习惯性的使用这个语言,这里说一下在流程编写过程中,Perl
中的流程控制语句。
看到网上有一篇很详细的介绍,我就学习了一遍,然后搬运一下加点自己的理解保存。
骏马金龙https://www.cnblogs.com/f-ck-need-u/p/9511878.html
布尔值控制
在perl中,布尔值判断比较简单。
对于数字,0表示假,其它所有数字都是真。
对于字符串,空字符串('')为假,其它所有字符串为真(有例外,见下一条)。
对于字符串'0',perl是当作数值0来处理的,所以这是唯一的非空但为假的字符串,一定要注意。
对于既不是数字,也不是字符串,那么先转换为数字或字符串再做判断(也就是"undef"表示假,其它所有引用表示真)。
"!"表示取反。
另外,perl还有个技巧,将两个"!"一起用,相当于"负负得正",所以原来是真的仍然是真的,原来是假的仍然是假的。但实际上,perl会将它们转换值"1"和"undef"。
$true = !!"perl"; # $true的值是1
$false1 = !!"0"; # $false1的值为空(undef)
$false2 = !!""; # $false2的值为空(undef)
print "$true"."\n";
print "$false1"."\n";
print "$false2"."\n";
条件判断:if和unless
if和unless都是条件控制语句,并且都支持else,elsif的句子。
if(condition){ # 或者 unless(condition)
command
}
if(condition1){ # 或者 unless(condition1)
command1
}elsif(condition2){
command2
}elsif(condition3){
command3
}
...
else{
commandN
}
if(condition){ # 或者 unless(condition)
command1
}else{
command2
}
这里的condition
可以是任意一个标量值,而且是基于布尔值的判断。
unless
和if
的判断方式相反,if
的condition
为真执行后面的代码,否则执行else
或者退出if
的结构。而unless
则是condition
为假的时候才执行后面的代码,否则执行else
或者退出unless
结构,在这里unless
相当于if
中的else
部分,或者相当于if(!condition)
·
三目(三元)运算符?:
perl
支持常见的三目运算符:如果expression
返回真,则整个表达式返回if true
,否则返回if false
,比较简单的使用:
expression ? if true : if false
$max = $a > $b ? $a : $b;
## 等于
if($a > $b){
$max = $a;
}
else{
$max = $b;
}
三目运算符也有复杂的使用:
vi test.pl
#!/usr/bin/perl
use warnings;
use strict;
use feature qw(say);
my $num = $ARGV[0];
$test = ($num < 10) ? "a" :
($num < 100) ? "bb" :
($num < 1000) ? "ccc" :
"dddd";
say $test;
测试结果为:
perl test.pl
a
perl test.pl 3
a
perl test.pl 99
bb
perl test.pl 119
ccc
perl test.pl 1199
dddd
逻辑运算符:and(&&)、or(||)、//、not(!)
expression1 < and or && || // > exxpression2
# && 运算符只有两边为真时才返回真,且短路计算:expression1为假时直接返回false,不会评估expression2
# || 运算符只要一边为真时就返回真,且短路计算:expression1为真时直接返回true,不会评估expression2
# and 和 or 基本等价于对应的 && 和 || ,但文字格式的逻辑运算符优先级非常低
$m < $n and $m = $n; # 以$m的姿态取出$m和$n之间较大值
not 和 ! 求反,同样文字格式的 not 的优先级很低
# 因为符号格式的逻辑运算符优先级很高,所以往往左边和右边都会加上括号,而文字格式的优先级很低,左右两边不需加括号
# // 运算符
perl 短路计算 和 // 运算符
perl的短路计算返回的是最后运算的表达式的值:
如果这个返回值对应的布尔值为真,则整个短路计算自然为真
如果这个返回值对应的布尔值为假,则整个短路计算自然为假
这个返回值有时候很有用,往往通过逻辑或的操作来设置默认值,所以,这个返回值既保证短路计算的结果不改变,又能得到返回值。比如:
my $test = $str || "test"
当$str
变量存在时,它返回真(0
是例外,见下面),并将$str
赋值给$test
;当$str
变量不存在时,它返回假,于是评估"test"
,因为是个字符串,所以返回真,于是将test
赋值给$test
。这样一来,$str
变量就有了默认值。
但是,如果$str
变量存在,但值为0
(字符串的或数值的),由于它也返回假,导致$test
被赋以"test"
。
这个操作最简单的还是三目运算符,改用下面这种先判断,再赋值的行为:
my $test = defined $str ? $str : "test";
但是这样的写法理解起来比较复杂,perl 5.10
版本提供了更方便的"逻辑定义或"(logical defined-or)操作符//
:当发现左边的值是已经定义过的,就直接进行短路计算,而不管该左边的值评估后是真是假。
my $test = $str // "test";
while循环和until循环
while(condition){
commands;
}
until(condition){
commands;
}
until和其它某些语言的until循环有所不同,perl的until循环,内部的commands主体可能一次也不会执行,因为Perl会先进行条件判断,当条件为假时就执行,如果第一次判断就为真,则直接退出until。
无限循环:
while(1){
commands;
}
for循环
for(my $i=1;$i<=10;$i++){
print $i,"\n";
}
for关键字后面括号中的3个表达式都可以省略,但两个分号不能省略:
如果省略第三个表达式,则表示一直判断,直到退出循环或者无限循环
如果省略第二个表达式,则表示不判断,所以会无限循环
如果省略第一个表达式,则表示不做初始赋值
Perl
中的for
也支持成员测试性的遍历,就像shell
中的for i in ...
的操作一样,它期待一个列表上下文,表示遍历整个列表。如果省略控制变量,表示使用$_
。例如:
my @arr = qw("Shell", "Python", "Perl", "PHP");
for $i (@arr){ print "$i\n" }
for (@arr) {print "$_\n"}
foreach循环
foreach更适合用于遍历,所有foreach都能直接修改关键字为for而转换成for循环。当写成for格式的时候,perl通过判断括号中的分号来决定这是foreach循环还是for的普通循环。但for能实现的循环功能,foreach不一定能实现,因为for中有初始变量,有条件判断,而foreach则是简单版的for循环。
先解释下foreach,例如,迭代从1到10的列表:
foreach $i (1..10){
print $i,"\n";
}
关于for循环和foreach循环,如果在遍历过程中修改了元素的值,它会直接修改原始值。换句话说,迭代时赋值给控制变量的元素的引用,而不是赋值元素再赋值给控制变量。
@arr=qw(perl python shell);
foreach $subject (@arr) {
$subject .= "aaa";
}
print @arr; # 输出perlaaapythonaaashellaaa
当foreach/for遍历结束后,控制变量将复原为foreach/for遍历前的值(例如未定义的是undef)。
$subject="php";
foreach $subject (qw(perl python shell)){
}
print $subject; # 输出"php"
each遍历
each用来遍历hash或数组,每次迭代的过程中,都获取hash的key和value,数组的index(数值,从0开始)和元素值。
each放在列表上下文,会返回key/value或index/element,放在标量上下文则只返回key或index。
遍历hash:
#!/usr/bin/perl -w
use strict;
my %hash = (
name1 => "longshuai",
name2 => "wugui",
name3 => "xiaofang",
name4 => "woniu",
);
while(my($key,$value) = each %hash){
print "$key => $value\n";
}
# 输出结果
name4 => woniu
name3 => xiaofang
name2 => wugui
name1 => longshuai
遍历数组:
#!/usr/bin/perl -w
use strict;
my @arr = qw(Perl Shell Python PHP Ruby Rust);
while(my($key,$value) = each @arr){
print "$key => $value\n";
}
# 输出结果
0 => Perl
1 => Shell
2 => Python
3 => PHP
4 => Ruby
5 => Rust
each放在标量上下文:
#!/usr/bin/perl -w
use strict;
my %hash = (
name1 => "longshuai",
name2 => "wugui",
name3 => "xiaofang",
name4 => "woniu",
);
my @arr = qw(Perl Shell Python PHP Ruby Rust);
while(my($key) = each %hash){
print "$key\n";
}
while(my($key) = each @arr){
print "$key\n";
}
# 输出结果
name2
name4
name3
name1
0
1
2
3
4
5
表达式修饰符(改写流程控制语句)
perl支持单条表达式后面加流程控制符。如下:
command OPERATOR CONDITION;
print "true.\n" if $m > $n;
print "true.\n" unless $m > $n;
print "true.\n" while $m > $n;
print "true.\n" until $m > $n;
print "$_" foreach @arr;
执行一次的语句块
使用大括号包围一段语句,这些语句就属于这个语句块,这个语句块其实是一个循环块结构,只不过它只循环一次。语句块也有自己的范围,例如可以将变量定义为局部变量。
{
print "Enter a Num","\n";
chomp(my $n = <STDIN>);
$res = sqrt $n;
print "$res","\n";
}
print $res,"\n";
循环控制:last、next、redo、LABEL(标签)
last相当于其它语言里的break关键字,用于退出当前循环块(for/foreach/while/until/执行一次的语句块都属于循环块),注意是只退出当前层次的循环,不会退出外层循环
next相当于其它语言里的continue关键字,用于跳入下一次迭代。同样只作用于当前层次的循环
redo用于跳转到当前循环层次的顶端,所以本次迭代中曾执行过的语句可能会再次执行
标签用于为循环块打上标记,以便那些循环块控制关键字(last/next/redo)可以指定操作的循环层次
以下是打标签的示例(标签建议采用大写):
#!/usr/bin/perl
use strict;
LINE: while(<>){
foreach(split){
last LINE if /error/i;
print "$_";
}
}
上面的标签循环中,首先读取一行输入,然后进入foreach遍历,因为split没有参数,所以使用默认参数所属范围是while循环,split以空格作为分隔符分割这一行,同时foreach也没有控制变量,所以使用默认的控制变量所属范围是foreach循环。当foreach的_。
例如,这个perl程序读取a.txt文件,其中a.txt文件的内容如下:
cat a.txt
hello world
hello world Error
hello world Error heihei
perl -w test.plx a.txt
hello
world
hello
world
附加循环代码:continue
perl中还有一个continue关键字,它可以是一个函数,也可以跟一个代码块。
continue # continue函数
continue BLOCK # continue代码块
如果指定了BLOCK,continue可用于while和foreach之后,表示附加在循环结构上的代码块。
while(){
code
}continue{
attached code
}
foreach () {
code
} continue {
attached code
}
每次循环中都会执行此代码块,执行完后进入下一循环。
在continue代码块内部,也可以使用redo、last和next控制关键字。所以,这几个流程控制关键字更细致一点的作用是:redo、last直接控制循环主体,而next是控制continue代码块。所以:
while(){
# redo jump to here
CODE
} continue {
# next jump to here
CODE
# next loop
}
# last jump to here
实际上,while和foreach在没有给定continue的时候,逻辑上等价于给了一个空的代码块,这时next可以跳转到空代码而进入下一轮循环。
#!/usr/bin/perl
use strict;
use warnings;
$a=3;
while($a<10){
if($a<6){
print '$a in main if block: ',$a,"\n";
next;
}
} continue {
print '$a in continue block: ',$a,"\n";
$a++;
}
# 输出结果
$a in main if block: 3
$a in continue block: 3
$a in main if block: 4
$a in continue block: 4
$a in main if block: 5
$a in continue block: 5
$a in continue block: 6
$a in continue block: 7
$a in continue block: 8
$a in continue block: 9
参考
—— dulunar 后记于 2019.11