1. 哈希切片

对哈希成员可以使用哈希切片(hash slice)的方式进行检索

my @three_scores = ($score{"barney"}, $score{"fred"}, $score{"dino"});
my @three_scores = @score{ qw/ barney fred dino / };

切片一定是列表,因此哈希切片也是用@符号来表示

为什么提到哈希的时候没有用到百分号符号(%)?因为百分号符号表示整个哈希,哈希切片(就像其它切片一样)本质上是列表而不是哈希,在perl中$代表单个东西,@代表一组东西,而%代表一整个哈希

my @players = qw/ barney fred dino /;
my @bowling_scores = (195, 205, 30);
@score{@players} = @bowling_scores;

最后一行所做的事相当于对($score{"barney"} , $score{"fred"}, $score{"dino"})这个具有3个元素的列表进行赋值

哈希切片也可以被内插进字符串

print "Tonight's players were: @players\n";
print "Their scores were: @score{@players}\n";

2. 键-值对切片

perl5.20开始引入了键-值对切片的概念,可以一次取出多个键-值对

之前,对于哈希切片要提取一组值,会写成这样

my @values = @score{@players};

哈希名字前所用的@表示我们需要返回一组值,这组值最后保存到数组@values内。如果要同步提取对应的键,还要额外再配对生成新的哈希

my %new_hash;
@new_hash{@players} = @values;

或者用map函数配对

my %new_hash = map {$_ => $score{$_}} @players;
use v5.20;
my %new_hash = %score{@players};

注意,这里变量名前的符号并非表示变量类型,我们只是用它指定提取数据的方式,这里的%表示按照哈希键-值对的方式返回,从而构造一个新的分片后的哈希

所以,即使是对数组变量也可以这么做,同时把数组下标当作哈希键返回

my %first_last_scores = %bowling_scores[0, -1];

2. 捕获错误

1. eval的使用

下面这些比较典型的错误语句都能让程序崩溃

my $barney = $fred / $dino; # 除零错误?my $wilma = '[abc'; # 非法的正则表达式?
print "match\n" if /\A($wilma)/;open my $caveman, '<', $fred # 用户提供的数据错误?or die "Can't open file '$fred' for input: $!";

要怎么检查字符串$wilma才能确保它引入的正则表达式合法呢?
perl提供了简单的方式来捕获代码运行时可能出现的严重错误,使用eval

eval( $barney = $fred / $dino);

现在即使$dino是0,这一行也不会让程序崩溃。只要eval发现在它的监察范围内出现致命错误,就会立即停止运行整个块,退出后继续运行后面的代码。
注意,eval块的末尾有一个分号。实际上,eval只是一个表达式,而不是类似while或foreach那样的控制结构。所以在监察的语句块末尾必须写上分号

eval的返回值就是语句块最后一条表达式的执行结果,这一点和子程序相同
所以,我们可以把语句块最后的$barney从eval里拿出来,将eval表达式的运行结果赋值给它。这样,声明的$barney变量就位于eval外部,便于后续使用

my $barney = eval { $fred / $dino };

如果eval捕获到了错误,那么整个语句将返回undef。可以使用定义或操作符对最终的变量设定默认值,比如NaN(表示“Not a Number”,非数字)

use v5.10;
my $barney = eval {$fred / $dino} // 'NaN';

当运行的eval块内出现致命错误时,停下来的只是这个语句,整个程序不会崩溃

当eval结束时,需要判断是否一切正常。如果捕获到致命错误,eval会返回undef,并在特殊变量$@中设置错误信息,比如:Illegal division by zero at my_program line 12 如果没有错误发生,$@就是空的。这时就可以通过检查$@取值的真假就可以判断是否有错误发生。所以,我们常常会看到eval语句块之后立即跟上这样一段检测代码

use v5.10;
my $barney = eval { $fred / $dino } // 'NaN';
print "I couldn't divide by \$dino: $@" if $@;

也可以通过检查返回值来判断,只要正常工作时能返回真值就可以了
不过最好是像下面这样写

unless(defined eval { $fred / $dino }){print "I couldn't divide by \$dino: $@" if $@;
}

有时候想测试的部分即使成功了也没有什么有意义的返回值,所以需要另外构造一个有返回值的代码块。如果eval捕捉到了错误,就不会执行最后一条语句,也就是单个数字形式的表达式1

unless( eval { some_sub();1}){print "I couldn't divide by \$dino: $@" if $@;
}

在列表上下文中,捕捉到的eval会返回空列表
下面这行代码中,如果eval失败的话,@averages最终只会得到两个值,因为eval返回的是空列表,等于不存在

my @averages = ( 2/3, eval{ $fred / $dino }, 22/7 );

eval语句和其它语句一样,所以可以设定其中变量的作用域

foreach my $person (qw/ fred wilma betty dino pebbles /){eval{open my $fh, '<', $personor die "Can't open file '$person': $!":my($total, $count);while(<$fh>){$total += $_;$count++;}my $average = $total/$count;print "Average for file $person was $average\n";&do_something($person, $average);};if($@){print "An error occurred($@), continuing\n";}
}

如果在处理foreach提供的列表中的某个文件时发生错误,会得到错误信息,但程序会继续执行下一个文件

可以把eval块写在另一个eval块里嵌套,内层的eval负责捕获它自己块中的错误,不会把错误泄露到外层块中

下面的代码把除0错误放在内层里单独捕获

foreach my $person (qw/fred wilma betty barney dino pebbles/){eval{open my $fh, '<', $personor die "Can't open file '$person': $!";my($total, $count);while(<$fh>){$total += $_;$count++;}my $average = eval{$total/$count} // 'NaN'; # 内层evalprint "Average for file $person was $average\n";&do_something($person, $average);};if($@){print "An error occurred($@), continuing\n";}
}

总共有4种错误时eval无法捕获的

  1. 出现在源代码中的语法错误,比如没有匹配的引号,忘写分号,漏写操作符,或者非法的正则表达式等
eval{print "There is a mismatched quote"\';my $sum = 42+;/[abc/;print "Final output\n"
};

perl解释器的编译器会在解析源代码时捕获这类错误并在运行程序前停下来,而eval仅仅能捕获perl运行时出现的错误

  1. 让perl解释器本身崩溃的错误,比如内存溢出或者收到无法接管的信号。这类错误会让perl意外终止运行,既然perl已经退出运行了,自然无法用eval捕获
  2. eval无法捕获警告,无论是用户发出的(warn函数),还是perl自己内部发出的。可以参考perlvar文档中的有关__WARN__伪信号的内容
  3. (最后一种其实不算是错误)exit操作符会立即终止程序运行,就算是在eval块内呼叫的,子程序内部也会立即终止

“eval字符串”
把其中的字符串直接当作perl源代码编程

my $operator = 'unlink';
eval "$operator \@files;";

2. 更高级的错误处理

每种语言都有一套自己处理错误的方式,但大多有一个称为异常(exception)的概念。具体来说,就是尝试运行某段程序,如果出现错误就抛出(throw)异常,然后等待后续负责接管处理(catch)这类异常的代码做相应的处理

perl种最基本的做法是:用die抛出异常,然后用eval接管,可以通过识别保存在$@中的错误信息来判断到底出了什么问题

eval{...;die "An unexpected exception message" if $unexpected;die "Bad denominator" if $dino == 0;$barney = $fred / $dino;
};
if($@ =~ /unexpected/){...;
}elsif($@ =~ /denominator/){...;
}

不过这样做的弊端有很多,最明显的事$@变量的动态作用域问题了。由于$@是一个特殊变量,所写的eval可能会被包含在另一个高层的eval里面,那就需要确保这里出现的错误和高层出现的错误不相混淆

{local $@; # 不和高层错误相混淆eval{...;die "An unexpected exception message" if $unexpected;die "Bad denominator" if $dino ==0;%barney = $fred / $dino;
};
if($@=~ /unexpected/){...;
}elsif($@ =~ /denominator/){...;
}
}

Try:;Tiny模块很好用!!!

use Try::Tiny;try{...; # 某些可能会抛出异常的代码
}
catch{...; # 某些处理异常的代码
}
finally{...;
}

这里try的作用类似于之前的eval语句,只有出现错误时,才会运行catch中的部分,不过最后都会运行finally的部分

不过catch或finally块时可以省略的,可以只用try语句并直接忽略出现的错误

my $barney = try {$fred / $dino};

可以用catch来处理错误,为了避免混淆$@,Try::Tiny把错误信息放到了默认变量$_里,不过还是可以访问$@

use v5.10;my $barney = try {$fred / $dino}catch{say "Error was $_"; # 不用$@ };
use v5.10;my $barney =try {$fred / $dino}catch{say "Error was $_"; # 不用$@}finally{say @_ ? 'There was an error' : 'Everything worked';};

3. 用grep筛选列表

选出列表中的部分成员

my @odd_numbers;foreach (1..1000){ # 从一大堆数字中挑出奇数push @odd_numbers, $_ if $_ % 2;
}my @odd_numbers = grep {$_ % 2 } 1..1000;

grep的第一个参数是代码块,其中$_是占位变量,代码块对列表的每个元素进行计算并返回真或假值。第二个参数是要被筛选的元素列表
从一个文件中取出包含fred的行

my @matching_lines = grep { /\bfred\b/i } <$fh>;my @matching_lines = grep /\bfred\b/i, <$fh>;

如果条件判断只是一个简单的表达式,而不是整个代码块,那么在这个表达式后面用逗号结束就可以了

grep操作符在标量上下文中返回的是符合过滤的元素个数

my @matching_lines = grep /\bfred\b/i, <$fh>;
my $line_count = @matching_lines;my $line_count = grep /\bfred\b/i, <$fh>;

4. 用map把列表数据变形

下面的代码将数字格式化为“金额数字”输出

# 以下为传统做法
my @data = (4.75, 1.5, 2, 1234, 6.9456, 12345678.9, 29.95);
my @formatted_data;foreach(@data){push @formatted_data, big_money($_);
}my @formatted_data = map {big_money($_)} @data;

map返回的不是逻辑真假值,而是该表达式实际计算的结果
而且map语句块实在列表上下文中求值的,所以每次可以返回一个以上的元素

任何形式的grep或map语句都可以改写成foreach循环,但中间需要借助临时数组保存数据

print "The money numbers are:\n",map {sprintf("%25s\n", $_)} @formatted_data;my @data = (4.75, 1.5, 2, 1234, 6.9456, 12345678.9, 29.95);
print "The money numbers are:\n",map {sprintf("%25s\n", big_money($_))} @data;print "Some powers of two are:\n",map "\t" . (2**$_) . "\n", 0..15;

Perl-高级perl技巧2相关推荐

  1. perl 正则表达式使用技巧

    Perl正则表达式语法 所谓Perl正则表达式,就是一串特别设计过的字符串,可以按照你的意图用匹配操作寻找你要求的目标.我这里不是Perl手册,也不是教科书,所以我从例子开始,具体的完整说明还请查手册 ...

  2. linux下perl命令行参数,Perl One-Liners | Perl命令行学习1 -e参数

    注:本内容需要点的perl编程基础,最好是读过<perl语言入门>. 本系列是自己平常学习工作中的总结,每一个实例均为我为了讲解而设置的,自己试过的,如有错误,望能见谅 Perl 命令行参 ...

  3. “阿一web标准学堂”选修课:EditPlus高级使用技巧(附视频、课件、代码下载)...

    阿一web标准学堂 <阿一web标准学堂>是一套面向web标准初学者的视频系列.虽然我也只是一个web标准刚入门的学习者,但是我还是希望能通过这个学堂将自己学习到的东西与大家分享,这样不仅 ...

  4. (011) Linux之高级键盘技巧

    十年运维系列之基础篇 - Linux 作者:曾林 联系:1494445739@qq.com 网站:www.jplatformx.com 版权:文章未经同意请勿转载 一.引言 这里指的高级键盘技巧其实有 ...

  5. idea调试debug技巧_这几个IDEA高级调试技巧,用了都说好!

    转载自: dwz.cn/zMaNp9Kf 本文将讲解的高级调试技巧如下: 条件断点 回到"上一步" 多线程调试 远程调试 临时执行表达式/修改变量的运行值 一.条件断点 循环中经常 ...

  6. python高级应用_Python高级编程技巧

    Python 高级编程技巧 本文展示一些高级的 Python 设计结构和它们的使用方法.在日常工作中,你可以根据需要 选择合适的数据结构, 例如对快速查找性的要求. 对数据一致性的要求或是对索引的要求 ...

  7. android 字体加粗有阴影,Android TextView高级显示技巧实例小结

    本文实例总结了Android TextView高级显示技巧.分享给大家供大家参考,具体如下: 1. 自定义字体 可以使用setTypeface(Typeface)方法来设置文本框内文本的字体,而And ...

  8. HiveSQL高级进阶技巧

    直接上干货,HiveSQL高级进阶技巧,重要性不言而喻.掌握这10个技巧,你的SQL水平将有一个质的提升! 1.删除: insert overwrite tmp select * from tmp w ...

  9. 6个高级Python技巧

    前言 Python是一种面向对象的语言,它与英语非常相似,因此对于初学者来说是一种非常好的语言.它的高级特性和受支持的库包甚至可以用几行代码来编写复杂的任务.在本文中,我们将介绍python的一些高级 ...

  10. 功能丰富的Perl:用Perl读写Excel文件

    功能丰富的Perl:用Perl读写Excel文件 2001年08月31日 16:00 来源:ChinaUnix文档频道 作者:HonestQiao 编辑:周荣茂 级别: 初级 Teodor Zlata ...

最新文章

  1. 【JetPack】kotlin-android-extensions 插件 ( 视图绑定简单用法 )
  2. Stanford UFLDL教程 从自我学习到深层网络
  3. go语言mysql删除记录_MySQL数据库删除操作-Go语言中文社区
  4. linux vim命令跳到67行,Linux学习之Vim/Vi使用(十三)
  5. String封装——读时共享,写时复制
  6. 我可以在输入字段上使用:before或:after伪元素吗?
  7. 初始化请求例子_当一个http请求来临时,SpringMVC究竟偷偷帮你做了什么?
  8. spring cloud常用组件
  9. FlexSlider插件的详细设置参数
  10. 王兴191条思考:一个顶级创始人的修炼与洞察
  11. css样式基础--基本选择器
  12. 双碳绿色风中,乘势而起了哪些新能源?
  13. LLM系列 | 01: 亲测ChatGPT最强竞品Claude,且无需翻墙、注册简单、免手机号
  14. WPS中如何将多个文件在不同窗口中打开
  15. 【NAS】Samba配置文件解析
  16. php微信自动回复接口,微信接口自动回复
  17. lange耦合器设计步骤_用于承受1000W的3dBLange耦合器及制备方法与流程
  18. php编译安装, 编译安装nginx, yum安装nginx, nginx虚拟主机,默认虚拟主机
  19. STM32WL无线Lora
  20. 科大讯飞Windows麦克风监听语音唤醒+语音识别demo

热门文章

  1. [Atcoder Yahoo Contest 2019]D.Ears(动态规划)
  2. 「软件合集」免费分享15款小众实用软件,没多少人知道,打包送你
  3. php狼人杀,《狼人杀专业术语》 一秒变成狼圈人!
  4. 混杂模式就安全了?--只谈配置混杂模式
  5. 定义申请OMA LwM2M Model Object
  6. Git冲突解决: git checkout高级用法
  7. 8086/8088 指令执行流水线断流原因
  8. spark agg算子使用
  9. “Let’s Eat Grandma”:标点符号(句法树)增强语义表达,用于情感分析
  10. 实现python调用Matlab的.m文件