Chinaunix首页 | 论坛 | 博客
  • 博客访问: 547465
  • 博文数量: 201
  • 博客积分: 7734
  • 博客等级: 少将
  • 技术积分: 1994
  • 用 户 组: 普通用户
  • 注册时间: 2010-04-09 19:18
文章分类

全部博文(201)

文章存档

2011年(28)

2010年(173)

分类:

2010-09-09 13:18:34

第十章其它控制结构:
unless控制结构:
在if控制结构中,只有当条件表达式为真的时候才执行某块代码,如果你想让程序块在条件为假的时候才执行,请把if改成unless。
 

#!/usr/bin/perl -w

unless ($fred =~ /^[A-Z_]\w*$/i) {
  print "The alue of \$fred doesn't look like a Perl identifier name.\n";
}
#上面这个程序和下面的一样的:

if ($fred =~/^[A-Z_]\w*$/i) {
#什么都不做

} else {
  print "The value of \$fred doesn't look like a Perl identifier name.\n";
}
#这两种写法的效率是一样的,他们应该会被编译相同的内部字节码。还有一个改成的方法,就是以取反操作!来否定条件。

if ( ! ($fred =~ /^[A-Z_]\w*$/i) ) {
  print "The value of \$fred doesn't look like a Perl identifier name.\n";
}

unless附带的else子句:
其实哪怕是在unless结构中也可以使用else语句,虽然支持这样的语法,但是可能会导致困惑:

#!/usr/bin/perl -w

unless ($mon =~ /^Feb/) {
  print "This month has at least thirty days.\n";
} lese {
  print "Do you see what's going on here?\n";
}
#如果用if语句我们可以写成这样:

if ($mon =~ /^Feb/) {
  print "Do you see what's going on here?\n";
} else {
  print "This month has at least thirty days.\n";
}

Until控制结构:
有时也许会想要颠倒while循环的条件。那么,使用until:

until ($j > $i) {
  $j *= 2;
}

这个循环会一直执行,直到条件为真,它和while的唯一差别在于until会在条件为假(而非真)时重复执行。因为条件判断发生在循环第一次迭代之前,所以它仍旧是一个执行零次以上的循环,和while循环一样。类似if和unless转化的例子。

条件修饰词:
为了进一步简化表达,表达式后面可以接着一个用来控制它的修饰词,例如用if修饰词来模拟一个if块。

===========================
#!/usr/bin/perl -w

$n = -1;
print "$n is a negative number.\n" if $n < 0;

和下面是一样的:
if ($n < 0) {
  print "$n is a negative number.\n";
}
===================================
@error("Invalid input") unless &valid($input);
$i *= until $i > $j;
print " ";, ($n += 2) while $n < 10;
&greet($_) foreach @person;

print " ", ($n += 2) while $n < 10;

while ($n < 10) {
  print " ", ($n += 2);
}
是一样的。

修饰词的两边都只能写单个表达式,因此不能写某事if某事while某事until某事unless某事foreach某事,
因为这样太让人困惑了。另外修饰词的左边也不能放多条语句。

祼块控制结构:
所谓祼块就是没有关键字或条件的代码块。有一个while循环:

while (condition) {
body;
body;
body;
}
#然后拿走关键字while和条件,就会得到一个祼块:

{
body;
body;
body;
}

为临时词法变量圈定有效范围:

{
  print "Please enter a number: ";
  chomp (my $n = <STDIN>);
  my $root = sqrt $n;
  print "Th square root of $n is $root.\n";
}

elsif 子句:
许多情况下,需要逐项检查一系列的条件表达式,看看其中哪个为真。这可以通过if控制结构的elsif子句来写成如下代码:

if ( ! defined $dino ) {
  print "The value is undef.\n";
} elsif ( $dino =~ /^-?\d+\.?$/) {
  print "The value is an integer.\n";
} elsif ( $dino =~ /^-?\d*\.\d+$/ ) {
  print "The value is a _sample_ floating-point number.\n";
} elsif ($dino eq '') {
  print "The value is the empty string.\n";
} else {
  print "The value is the string '$dino'.\n";
}

如果没有任何一项符合,则执行最末端的else块。这样如果有一百个elsif的子句,必须执行前面的99个失败的测试,
才会达到第一百个。这样效率会很低。可以用case或switch来处理同样的问题。在5.10或者更高版本用户还可以选择
given-when。

自增和自减:
使用自增操作符++能对标量加1,变成43。

#!/usr/bin/perl -w

my $bedrock = 42;
$bedrock++;
print $bedrock;

跟其他将变量加1的方法一样,标量若未定义将会被创建:

#!/usr/bin/perl -w

my @people = qw{ Fred barney Fred wilma dino barney Fred pebbles };
my %count;                        
#新的空哈希

$count{$_}++ foreache @people;    
#按需要创建新的键/值对

=================================
[server1@root perl]
# cat sss

#!/usr/bin/perl -w

my @people = qw{ Fred barney Fred wilma dino barney Fred pebbles };
my %count;
$count{$_}++ foreach @people;
print %count,"\n";

[server1@root perl]
# perl sss

pebbles1barney2dino1wilma1Fred3
=================================
$bedrock--;        

自增的值:
可以获得变量的值并且同时进行修改。把++操作符写在变量之前就能先增加变量的值,然后获取变量的值。这是前置自增:

#!/usr/bin/perl -w

use strict;
my $m = 5;
my $n = ++$m;
print "$n\n";    
#结果为6    加1

========================
#!/usr/bin/perl -w

use strict;
my $m = 5;
my $c = --$m;
print "$c\n";    
#结果为4 减1

========================
#!/usr/bin/perl -w

use strict;
my $m = 5;
my $d = $m++;
print "$d\n";    
#结果为5 不变

========================
#!/usr/bin/perl -w

use strict;
my $m = 5;
my $e = $m--;
print "$e\n";    

说明,当将变量名放在前面会先取得值,之后再自增或自减。这种做法称为后置自增或后置自减:

如果你写的表达式中只有自增或自减,不使用值而只是利用副作用,前置后置交没有任何区别:
这类操作符的一个常见用法就是(配合哈希计数)判断之前已经见过的元素:

#!/usr/bin/perl -w

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{$_}++;
}

for控制结构:

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

for这种类型的循环事实上只是一种变相的while循环:如下。

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

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

for ( $i = 1; $i <= 10; $i++) {        #从1数到10

 print "I can count to $i!\n";
}
=======================
[server1@root perl]
# cat for1

#!/usr/bin/perl -w

use strict;
use warnings;
my $i;
for ( $i=1 ; $i <= 10; $i++) {
print "I can count to $i\n";
}
[server1@root perl]
# perl for1

I can count to 1
I can count to 2
I can count to 3
I can count to 4
I can count to 5
I can count to 6
I can count to 7
I can count to 8
I can count to 9
I can count to 10

事实上这三个循环控制部分(初始化、测试和递增)都可以为空,但是即使不需要它们也还得保留分号。在下面这个极不寻常的例子里,测试条件是一个替换,而递增则是空:

#!/usr/bin/perl -w

for ($_ = "bedrock";s/(.)//; ) {    
#当s///这个替换成功时,循环继续

  print "One character is: $1\n";
}
===============
[server1@root perl]
# perl for2

b
e
d
r
o
c
k

在测试表达式为空的时候,两个连续的分号会被强行解释为真,从而导致死循环。但是在你学到本章的后面,能自如地中断这种循环之前,请不要尝试:

#!/usr/bin/perl -w

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

foreach 和 for 之间的秘密:
事实上,在Perl的解析器里,foreach和for这两个关键字是等价的。也就是说,当Perl看到其中之一时,也就好像看到了另一个。
Perl可以从圆括号里的内容判断出你的意图。如果里面有两个分号,它就是刚才见到你的for循环。若是没有分号就是一个纯正的
foreach循环。

循环控制:
目前我们已经感觉到,Perl是一种所谓的结构化编程语言。特别是Perl程序的任何都只有一个入口,也就是块的顶端。不过相比前面介绍过的结构,有时候需要提早退出某块代码。Perl有三个循环控制操作符,我们可以在循环里使用它们,让循环非常灵活。

last操作符:
操作符last能立即中止循环。就像在C一类语言中的break操作符一样。它是循环的紧急出口。当你看到last,循环就会结束,例如:

#打印出所胡提到fred的行,直到碰到__END__记号:

=======================
#!/usr/bin/perl -w

use strict;
use warnings;
while (<STDIN>){
if (/__END__/) {
last;
} elsif (/fred/) {
print;
}
}
=======================

在Perl中有5种循环块。也就是for foreach while until 以及裸块
而if块或者子程序带的字括号不是循环的块,如同前面的例子,last操作符对整个循环块起作用。

last操作符只会对运行中最内层的循环块发挥作用。要跳出外层块,请继续看下去,我们很快就会提到。

next操作符:
有时个并不需要立刻退出循环,但是需要立刻结束当前这次迭代。这就是next操作符的用处,它会跳到内层循环块的底端。在next之后,程序将会断续执行循环的下次迭代,这和C-类语言中continue操作符的功能相似:

#分析输入文件中的单词:

while (<>) {
  foreach (split) {
#将$_分解成单词,然后每次将一个单词赋值给$_

    $total++;
    next if /\W/;    
#如果碰到不是单词的字符,跳过之后的表达式

    $valid++;
    $count{$_}++;    
#分别统计每个单词出现的次数

    
##上面的next语句如果运行,会跳到这里##

    }
    }
print "total things = $total, valid words = $valid\n";
foreach $word (sort keys %count) {
  print "$word was seen $count{$word} times.\n";
  }
===============================
#!/usr/bin/perl -w

while (<>) {
  foreach (split) {
    $total++;
    next if /\W/;
    $valid++;
    $count{$_}++;
    }
    }
print "total things = $total, valid words = $valid\n";
foreach $word (sort keys %count) {
  print "$word was seen $count{$word} times.\n";
  }
===============================

redo操作符:
循环控制族第三个成员是redo。它能将控制返回到本次循环的顶端,不经过任何条件测试,也不会进入下一次循迭代。而那些C一类语言的人却会对这个操作符感觉陌生。因为那些语言里没有这个概念。

#打字测试

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";

和另外两个操作符一样,redo在这5种循环块里都可以使用,并且在循环块嵌套的情况下只对最内层的循环起作用。

next和redo两者之间最大的区别在于next会正常继续下一次迭代,而redo则会重新执行这次迭代。下面的程序可以让你体验这三种
操作符工作方式的区别:

#!/usr/bin/perl -w

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";

带标签的块:

三目操作符?:
三目操作符就像一个if-then-else控制表达式。由于使用时需要三个操作数,所以称为三目操作符。它看起来像这样:

条件表达式 ? 真表达式 : 假表达式

首先,执行条件表达式,看看究竟是真还是假。如果为真,则执行真表达式;否则,就执行假表达式。每次使用时都会执行问号右边两个表达式中的一个,另一个则会跳过。换句话说,若条件表达式为真,则第二个表达式会被求值返回,忽略第三个表达式;倘若条件表达式为假,则忽略第二个表达式,而对第三个表达求值并返回。

逻辑操作符:

if ( $dessert{ 'cake' } && $dessert{ 'ice cream'}) {
#两个条件都为真

print "Hooray! Cake and ice cream!\n";
} elsif ($dessert{'cake'} || $dessert{'ice cream'}} {
#至少一个条件为真

print "That's still good...\n";
} else {
#两个条件都为假--什么也不干

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

短路操作符的值:
与C一类语言不同的地方在于,Perl的短路操作符求得的值不只是简单的布尔值,而是最后运算的那部分表达式的值。
这恰好和布尔值兼容,因为最后部分的值若是真,整个就是真,反之亦然。

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

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

||// 的区别。(搞死我了,搞了一下午)
no matter if that value on the lefthand side is true
or false. Even if someone’s last name is 0, this version still works:

如下比较:
1、
=========================
#!/usr/bin/perl -w

my %last_name = (
  "fred" => "flintstone",
  "dino" => undef,
  "barney" => "rubble",
  "linscora" => "" ,
  "qinghua" => "",
  "betty" => "rubble",
  );
my $last_name = $last_name{'linscora'} // '(No last name)';
my $last_name1 = $last_name{'qinghua'} || '(No last name)';
print "$last_name\n";
print "$last_name1\n";
====
[server1@root bin]
# ./perl /perl/notun

                            
#这行是//出来的效果。

(No last name)                
#这行是||出来的效果。

#0或undef的话对于|| 就是第一个不匹配 去匹配后面的

#但是0对于// 来说就是,第一个已经匹配了


#或这样说:第一个是当$last_name{$someone}等于0或空字符串时会把No last name赋值给$last_name;第二个只有$last_name{$someone}在为undef时才会把No last name赋值给$last_name


打完这几行字,头还是晕的。看来我的脑子真是不好用啊!哎


2、
====================
#!/usr/bin/perl -w

use 5.010;
foreach $try ( 0, undef, '0', 1, 25) {
    print "Trying [$try] ---> ";
my $value = $try // 'default';
say "\t got [$value]";
}
===========
Trying [0] --->      got [0]
Use of uninitialized value $try in concatenation (.) or string at /perl/notun line 4.
Trying [] --->      got [default]
Trying [0] --->      got [0]
Trying [1] --->      got [1]
Trying [25] --->      got [25]
==================

使用部求值操作符的控制结构:
前面学习了4个操作符&& || // 和?: 都有一个共性:根据左边的求值决定是否计算右边的表达式。有些情况会执行的表达式,
在另外的情况下并不执行。因此被统称为部分求值操作符,部分求值操作符是天然的控制结构,因为不会执行所有的表达式。

习题:

#!/usr/bin/perl -w

use strict;
my $secret = int ( 1 + rand 100);
print "Don't tell anyone, but the secret number is secret.\n";
while (1) {
  print "Please enter a guess from 1 to 100: ";
  chomp(my $guess = <STDIN>);
if ($guess =~ /quit|exit|^\*$/i) {
  print "Sorry you gave up. The number was $secret.\n";
  last;
} elsif ( $guess < $secret ) {
  print "Too small, Try again!\n";
} elsif ( $guess == $secret ) {
  print "That was it!\n";
  last;
} else {
  print "Too large. Try again!\n";
 }
}
=====================
#!/usr/bin/perl -w

use 5.010;
$ENV{ZERO}    = 0;
$ENV{EMPTY}    = '';
$ENV{UNDEFINED} = undef;

my $longest = 0;
foreach my $key ( keys %ENV)
  {
   my $key_length = length( $key );
   $longest = $key_length if $key_length > $longest;
}

foreach my $key (sort keys %ENV )
   {
   printf "%-${longest}s %s\n", $key, $ENV{$key} // "(undefined)";
   }
   
   printf "%-${longest}s %s\n", $key, $ENV{$key} ? $ENV{$key}: "(undefined)";


阅读(1189) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~