本帖最后由 PerlMonk 于 2017-4-18 12:43 编辑
列表操作
",", sort,reverse,push, pop, shift, unshift, grep, map
使用 grep 过滤列表
grep 的使用可以分为表达式形式和 block 形式
筛选大于10的数并保存到另一个数组:
- my @input_numbers = (1, 2, 4, 8, 16, 32, 64);
- my @bigger_than_10 = grep $_ > 10, @input_numbers;
复制代码 结果为 16, 32, 64
通过隐式引用来筛选末尾含有4的数字:- my @end_in_4 = grep /4$/, @input_numbers;
复制代码 如果测试表达式较为复杂,可以写在一个子例程中,然后通过 grep 调用。
在一组数字中,提取个位十位... 相加 %2 余 1 的项:- my @odd_digit_sum = grep digit_sum_is_odd($_), @input_numbers;
-
- sub digit_sum_is_odd {
- my $input = shift;
- my @digits = split //, $input; # Assume no nondigit characters
- my $sum;
- $sum += $_ for @digits;
- return $sum % 2;
- }
复制代码 块形式(相比调用子例程的形式,少了 return。在这里使用 return 将退出 grep ):- my @odd_digit_sum = grep {
- my $sum;
- $sum += $_ for split //;
- $sum % 2;
- } @input_numbers;
复制代码
使用 map 转换列表
- my @input_numbers = (1, 2, 4, 8, 16, 32, 64);
- my @result = map $_ + 100, @input_numbers;
复制代码 以及 map 没有规定对于每一项只返回一个值- my @result = map { $_, 3 * $_ } @input_numbers;
复制代码 借此可以快速从一个列表生成一组哈希映射,以便于做字典判断- my %hash = map { $_, 1 } @castaways;
-
- my $person = 'Gilligan';
- if( $hash{$person} ) {
- print "$person is a castaway.\n";
- }
复制代码
eval
- my $average = $total / $count; # divide by zero?
- print "okay\n" unless /$match/; # illegal pattern?
-
- open MINNOW, '>', 'ship.txt'
- or die "Can't create 'ship.txt': $!"; # user?defined die?
-
- implement($_) foreach @rescue_scheme; # die inside sub?
复制代码 其中每一行都有可能出错导致程序崩溃,但在实际应用中并不意味着应该结束整个程序,Perl 通过 eval 实现错误捕获:- eval { $average = $total / $count } ;
- print "Continuing after error: $@" if $@;
复制代码 当 eval 代码块运行出错时,错误信息保存到 $@,eval 之后的代码继续运行。注意 eval 不是结构语句,末尾必须加分号。
eval 语句块也可以像函数一样 return,如果出错,返回空值(在标量环境返回 undef,在数组环境返回空列表)。现在可以安全地处理零除错误:- my $average = eval { $total / $count };
复制代码 $average 要么是"商"要么是"undef"。
注意
Perl 允许 eval 镶嵌使用。
eval 可以捕获一般的错误,但无法处理结束进程、内存溢出等情况
Try::Tiny- use Try::Tiny;
- my $average = try { $total / $count } catch { "NaN" };
复制代码
执行动态生成的代码
eval 除了代码块的形式,还有一种字符串形式。在运行时编译运行某段字符串内的代码。这将带来一定风险,或许会执行带有攻击性的代码。
一个简短的示例:
- eval '$sum = 2 + 2';
- print "The sum is $sum\n";
复制代码 因为 eval 能够返回最后一句代码的结果,所以不必将赋值放在待执行的字符串中- foreach my $operator ( qw(+ ? * /) ) {
- my $result = eval "2 $operator 2";
- print "2 $operator 2 is $result\n";
- }
复制代码 和代码块形式一样,如果执行错误,有关信息将保留到 $@:- print 'The quotient is ', eval '5 /', "\n";
- warn $@ if $@;
复制代码 >The quotient is
>syntax error at (eval 1) line 2, at EOF
提醒
请谨慎使用 eval 执行字符串代码的形式,尽可能使用其他方法达到目的。在11章将介绍如何加载外部文件代码并执行,并使用更好的方法。
do
do 是 Perl 语言中一个强有力但容易被忽视的工具,用于将多个表达式组织到一个代码块中,
并像子例程一样返回最后执行的结果。
使用 do 语句块简化赋值判断
假设要为 $bowler 赋值,但是分为三种条件,需要写出多个 $bowler:
- my $bowler;
- if( ...some condition... ) {
- $bowler = 'Mary Ann';
- }
- elsif( ... some condition ... ) {
- $bowler = 'Ginger';
- }
- else {
- $bowler = 'The Professor';
- }
复制代码 如果改为 do 语句块的形式,只需要一个 $bowler- my $bowler = do {
- if( ... some condition ... ) { 'Mary Ann' }
- elsif( ... some condition ... ) { 'Ginger' }
- else { 'The Professor' }
- };
复制代码
一次读取文件
do 能够创建一个包围作用域,当我们要一次读取整个文件内容到变量中,可以通过 do block 私有作用域,为 $/ 和 @ARGV 创建私有副本,从而能够使用 <> 句柄读取 $filename 参数的文件内容
- $filename = __FILE__;
-
- my $file_contents = do {
- local $/;
- local @ARGV = ( $filename );
- <>;
- };
-
- print $file_contents;
复制代码
加载运行其他脚本代码
类似 eval,do 也有将字符串作为参数的形式,当传递的是字符串而非代码块时,do 假设传入的是文件,并从文件中读取代码编译执行:
复制代码 类似于 `eval "type slurp.pl";` 区别参考 perldoc -f do
缺点在于:即使执行的代码发生错误,程序仍会继续运行。并且即使是加载运行过的文件,仍会再次执行(对比 require)。基于这些原因,很少人使用 do 语句
我们知道,使用 use 加载模块,以及 use 语句在编译时运行。其实还可以通过 require 在运行时加载模块:复制代码 use List::Util 的实质是在 BEGIN 块中执行 require 以及 该模块的 import() 方法;- BEGIN {
- require List::Util;
- List::Util->import( ... );
- }
复制代码 通常 use 使用模块名的形式,而 require 还可以用文件名作为参数,导入文件:复制代码 require 能够记住已经加载过的文件,对于重复的加载将不会再执行 ( 对比 do )。更多内容参考 12 章 - Creating Your Own Perl Distribution
[Finished in 0.1s] |