Perl-其他控制结构2

1. 自增与自减

my $bedrock = 42;
$bedrock++; # $bedrock 加1,变成43

和其它将变量加1的方法一样,标量若未定义将会被自动创建

my @people = qw{ fred barney fred wilma dino barney fred pebbles };
my %count; # 新的空哈希
$count{$_}++ foreach @people; # 按需要创建新的键-值对

第1次处理foreach循环时,$count{$_}会自增。这是个快速而简易的方法,可用来检查列表中有哪些元素并计算每个元素出现的次数

$bedrock--; # $bedrock 减1,又变回42了

2. 自增的值

取得变量值的同时修改变量值

my $m = 5;
my $n = ++$m; # 先增加$m的值到6,再把该值赋给$n

或者把–操作符放在变量之前,先自减,再取新值。我们把这种操作称为前置自减

my $c = --$m; # 先减少$m的值到5,再把该值赋给$c

将变量名称放在前面就表示先取值,然后再自增或自减。这样的操作我们称为后置自增或后置自减

my $d = $m++; # $d 得到的是$m之前的值(5),然后$m增加到6
my $e = $m--; # $e 得到的是$m之前的值(6),然后$m增加到5

如果表达式中只有变量自增或自减操作,但不取新值,只是利用连带的该值功能的话,那么操作符前置或后置都一样,没有任何区别

$bedrock++;
++$bedrock;
my @people = qw{ fred barney bamm-bamm wilma dino barney betty pebbles };
my %seen;

foreach(@people){
	print "I've seen you somewhere before, $_!\n" if $seen{$_}++;
}

这类操作符的一个常见用法就是判断之前是否见过某个元素,一般会借助哈希计数

3. for控制结构

for(initialization; test; increment){
	body;
	body;
}

对perl而言,这种类型的循环事实上是一种变相的while循环

initializtion;
while(test){
	body;
	body;
	increment;
}

for循环目前最常见的用途就是控制重复的运算过程

for ($i = 1; $i <= 10; $i++){
	print "I can count to $i\n";
}

这是一个将$i从1数到10的循环
循环结束之后,它的控制变量取值会在范围之外。for循环非常灵活,可以用来进行各式各样的计数。

for($i = 10; $i >=1; $i--){
	print "I can count down to $i\n";
}

for($i = -150; $i <= 1000; $i += 3){
	print "$i\n";
}

这3个循环控制部分(初始化、测试和递增)都可以为空,但即使不需要它们也得保留分号

for($_ = "bedrock"; s/(.)//;){ # 当s///这个替换成功时,循环继续
	print "One character is: $1\n";
}

在这个例子里第1次执行循环时,替换操作会拿走bedrock中的b字母。每次执行循环会拿走1个字母,直到字符串为空。这时替换操作会失败,导致循环结束。
在测试表达式为空时,2个连续的分号会被强行解释为真,从而导致死循环。

for(;;){
	print "It's an infinite loop!\n";
}

更具perl风格的死循环

while(1){
	print "It's another infinte loop!\n";
}

如果不小心进行死循环,试试按control+c终止它

4. foreach和for之间的秘密关系

在perl解释器,foreach和for这2个关键字实际上是等价。如果里面有2个分号,它就是之前介绍的for循环,如果没有分号,就说明它是1个foreach循环

for(1..10){ # 实际上就是从1到10的foreach循环
	print "I can count to $_!\n";
}

在perl世界里面,纯正的foreach循环几乎总是更好的选择

for($i =1; $i < 10; $i++){ # 糟糕!这里有错!我们想计数到10,但实际上只能计数到9
	print "I can count to $_!\n";
}

这段代码实际上只能计数到9,这种错误叫做单步偏差(off by one)

5. 循环控制

perl程序的任何块都只有1个入口,也就是块的顶端。perl有3个循环控制操作符,可以在循环里使用它们,灵活控制代码流向。

1. last操作符

last操作符能立即中止循环的执行,就像在c语言中的break操作符一样。它是循环的紧急出口。

while(<STDIN>){
	if(/__END__/){
	#碰到这个计好说明再也没有其它输入了
		last;
	} elsif(/fred/){
		print;
	}
}
##last之后会跳到这里##

perl中有5种循环块,for、foreach、while、until、裸块if块或子程序带的花括号不是循环块
last操作符只会对当前运行的最内层的循环块发挥作用

2. next操作符

next操作符会立即结束当前这次循环迭代,执行循环的下一次迭代,这就像是c语言中的continue操作符

# 分析输入文件中的单词
while(<>){
	foreach(split){ # 将$_分解成单词,然后每次将1个单词赋值给$_
		$total++;
		next if /\W/; # 如果碰到不是单词的字符,跳过循环的剩余部分
		$valid++;
		$count{$_}++; # 分别统计每个单词出现的次数
		##上面的next语句如果运行,会跳到这里##
	}
}

print "total things = $total, valid words = $valid\n";
foreach $word(sort key %words){
	print "$word was seen $count{$word} times.\n";
}
3. redo操作符

redo操作符能控制返回到当前循环块的顶端,不经过任何条件测试,也不会进入下一次迭代循环

# 打字测试
my @words = qw{ fred barney pebbles dino wilma betty };
my $errors = 0;

foreach (@words){
	###redo操作符会跳到这里##
	print "Type the word '$_': ";
	chomp(my $try = <STDIN>);
	if($try ne $_){
		print "Sorry - That's not right.\n\n";
		$errors++;
		redo; # 跳回到循环的七点
	}
}
print "You've completed the test, with $errors errors.\n";

next和redo两者间最大的区别在于,next会正常继续下一次迭代,而redo会重新执行这次迭代

foreach(1..10){
	print "Iteration number $_.\n\n";
	print "Please choose: last, next, redo, or none of the above?";
	chomp(my $choice = <STDIN>);
	print "\n";
	last if $choice =~ /last/i;
	next if $choice =~ /next/i;
	redo if $choice =~ /redo/i;
	print "That wasn't any of the choices... onward!\n\n";
}

print "That's all, folks!\n";
4. 带标签的块

当你需要,从内层对外层的循环块进行控制时,可以使用标签(label)。在perl里,标签和其它标识符一样,是由字母、数字和下划线组成的,但不能以数字开头。然而由于标签没有前置符号,可能和内置函数或自定义子程序名混淆,所以一般用全大写字母命名标签。

要对某个循环块加上标签,通常只要将1个标签及1个冒号放在循环前面就行了。之后在循环里的last、next或redo后面加上这个标签就可以定向

LINE: while(<>){
	foreach(split){
		last LINE if /__END__/; # 跳出标签为LINE的循环
		...
	}
}

为了增加可读性,一般建议把标签靠左写,哪怕当前的代码的层次缩进很深。注意:标签应该用来命名整块代码,而不是用来标明程序中的某个具体位置
通常以名词来为循环命名

LINE: while(<>){
	WORD: foreach(split){
		last LINE if /__END__/; # 跳出LINE循环
		last WORD if /EOL/; # 忽略本行剩下的单词
		...
	}
}

6. 条件操作符

也叫做三目操作符
expression ? if_true_expr : if_false_expr

my $location = &is_weekend($day) ? "home" : "work";

my $average = $n ? ($total/$n) : "-----";
print "Average: $average\n";

my $average;
if($n){
	$average = $total / $n;
}else{
	$average = "-----";
}
print "Average: $average\n";


my $size =
	($width < 10) ? "small" :
	($width < 20) ? "medium" :
	($width < 50) ? "large" :
		"extra-large"; # 默认值

实际上这是由3层嵌套的?:操作符组成的

7. 逻辑操作符

perl拥有全套的逻辑操作符,可以用来对付布尔(真/假)值
常用来进行联合逻辑测试的逻辑与操作符&&和逻辑或操作符||

if($dessert{'cake'} && $dessert{'ice cream'}){
	# 两个条件都为真
	print "Hooray! Cake and ice cream!\n";
}elsif{
	# 至少一个条件为真
	print "That's still good...\n";
}else{
	# 两个条件都为假,什么都不干
}

perl在这里可能会走捷径,如果逻辑与操作符左边的表达式为假,整个表达式就不可能为真,因为必须两边都为真才会得到真。因此这时不必再检查右边的表达式,从而避免对其求值。

if((9 <= $hour) && ($hour < 17)){
	print "Aren't you supposed to be at work...?\n";
}

if(($name eq 'fred') || ($name eq 'barney')){
	print "You‘re my kind of guy!\n";
}

因为这个特性,这种操作符被称为==“短路”逻辑操作符==,它们只要有可能就会走捷径来获得结果。事实上,依赖这种短路行为的代码很常见,比如求得平均值的程序。

if(($n != 0) && ($total/$n < 5)){
	print "The average is below five.\n";
}

这个例子中,右边的表达式只有在左边为真的时候才被求值,因此程序不会因为意外的“除以0”而崩溃

8. 短路操作符的返回值

perl的短路操作符求得的值不只是简单的布尔值,而是最后运算的那部分表达式的值。
这个返回值很有用,我们常常利用逻辑或操作符提供变量的默认取值

my $last_name = $last_name{$someone} || `(No last name)`;

如果$someone在哈希中并不存在,左边的计算结果就是undef,也就是假。所以逻辑或操作符必须对右边的表达式求值,使它称为左侧变量的默认值。在这种习惯用法中,不光undef时会使用默认值,其他求值结果等效于假的情况也会被替换为默认值。如果要只在未定义情况下才提供默认值的初始化,可以把它改成使用条件操作符的写法

my $last_name = defined $last_name{$someone} ?
	$last_name{$someone} : '(No last name)';

9. 定义或操作符

||操作符能用于提供变量的默认值,但没有考虑到特殊情况,就是已定义的假值也可能会被意外地替换为默认值
perl 5.10引入了定义或(defined-or)操作符//,在发现左边的值属于已定义时进行短路操作,而非根据该值是逻辑真还是逻辑假。

use v5.10;

my $last_name = $last_name{$someone} // '(No last name)';

有时候需要给1个未定义变量赋值,若已定义则保留原值

use v5.10;

my $Verbose = $ENV{VERBOSE} // 1;
print "I can talk to you" if $Verbose;

foreach my $try (0, undef, '0', 1, 25){
	print "Trying [$try] ---> ";
	my $value = $try // 'default';
	say "\tgot [$value]";
}

输出显示只有在$try是undef时才会收到default的字符串

Trying [0] --->   got [0]
Trying [] --->   got [default]
Trying [0] --->   got [0]
Trying [1] --->   got[1]
Trying [25] --->   got[25]

一种很有用的写法

use v5.10;
use warnings;

my $name; # 没有值,属于未定义!
printf "%s", $name // '';
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值