Perl语言入门笔记 第十七章 高级perl技巧(eval, grep, map)
=pod 第十七章 高级perl技巧 切片: my (undef, $card_num, undef, undef, undef, $count) = split /:/; 定义undef的话,会默认忽略匹配的变量 更好的方法: 列表切片 my $mtime = (stat $some_file)[9]; #取得文件的第10个属性 my $card_num = (split /:/)[1]; my $count = (split /:/)[5]; 一次获得两个值: my ($card_num, $count) = (split /:/)[1, 5]; #取出1,5 从列表中取出第一个和最后一个元素,借助索引-1代表最后一个元素这一事实: my ($first, $last) = (sort @names)[0, -1]; my @numbers = (@names)[9, 0, 2, 1, 0]; 数组切片: 切片总是一个列表,所以数组切片总是使用一个@符号来标示,当你看见类似@names[...]之类的写法时,需要以perl的习惯来看开头的符号和结尾的方括号,方括号意味着你要检索数组成员,@符号则意味着获取的是整个列表,而$符号意味着获取单个元素。 但有一个切片可以工作,列表却不能的场合,那就是切片可以被直接内插到字符串中去: my @names = qw{zero one three four five six seven eight nine}; print "Bedrock @names[9, 0, 2, 1, 0]"; 如果我们想要内插@names,就会得到数组所有成员构成的字符串,元素之间用空格隔开,如果我们要内插的是@names[9, 0, 2, 1, 0], 就会得到指定数组元素构成的字符串,同样用空格隔开,让我们回到Bedrock图书馆的例子,假设我们的程序需要修改读者Slate先生的地址和电话号码,因为他刚刚搬到了Hollyrock山庄的某间大房子,如果我们得到一个存有关于他的信息列表的@items,那么就可以按如下方式简单的修改数组中的那两个元素: my $new_home_phone = "555-6099"; my $new_address = "99380 Red Rock West"; @items[2, 3] = ($new_address, $new_home_phone); 哈希切片: 和数组切片相似,也可以用哈希切片(hash slice)的方式从哈希里切出一些元素,还记得三个选手的保龄球积分吗?她们存放在哈希%score中,我们可以用哈希元素所构成的列表来取出这些积分,或是使用切片,这两个技巧实际效果相当,但第二种做法更加简洁高效: my @three_scores = ($score("barney"), $score{"fred"}, $score{""dino}); my @three_scores = @score{qw/barney fred dino/}; 切片一定是列表,因此哈希切片也是用@符号来标示。 my @player = qw(barney fred dion); my @bowling_scores = (195, 205, 30); @sorce{@player} = @bolwing_socres; 哈希切片也可以被内插进字符串: print "Tonight's Player were: @players\n"; print "Their scores were: @score{@players}"; 捕获错误: 用eval: 检查运行时错误,用eval包起来 eval{$barney = $fred / $dino}; 现在即使是$dino为0也不会造成程序崩溃 eval的返回值就是运距块中最后一条表达式的执行结果,常这样写: my $barney = eval {$fred / $dino}; print "I couldn't divie by \$dino:$@" if $@; unless(eval{$fred / $dino}) { print "I couldn't divide by \$dino: @$" if @!; } 总共有4种类型的错误是eval无法捕获的: 第一种:是出现在源代码中的语法错误,比如没有匹配的引号,忘写分号,漏写操作符,或者非法的正则表达式等。 第二种:是让perl解释器本身崩溃的错误,比如内存溢出或者受到无法接管的信号。 第三种:是eval无法捕获的错误是警告,不管是由用户发出的(通过warn函数),还是perl自己内部发出的(通过打开-W这个命令行选项,或者使用use warning编译指令。要让eval捕获警告专门的一套机制,请参考perl文档中_WARN_伪信号相关的内容) 第四种:是exit操作符会立即终止程序运行,就算从eval块内部的子程序来调用它 为了安全不要在程序里用eval,只有在相当关注安全时才应该使用eval。 任何出现在字符串中的东西都会被当做perl代码来解释执行: my $operator = 'unlink'; eval "operator \@files"; 更高级的错误处理: eval{ die "adfsasdfas" die "adfasd";;}; Try::Tiny模块从CPAN下载: use Try::Tiny; try { } catch { } finally { 这永远被执行到,以便实施清理工作 } autodie: use autodie; open my $fh, '>', $filename; #仍旧会在错误发生时调用die函数 use autodie qw(open system:socket); grep筛选列表: 得到奇数: my @odd_numbers = grep($_ % 2) 1..1000; 从文件中取出包含fred的行: 得到符合的行结果: my @matching_lines = grep{/\bfred\b/i}<$fh>; my @matching_lines = grep /\bfred\b/i, <$fh>; 得到符合的行的数量: my $line_count = grep /\bfred\b/i, <$fh>; 这就是上下文不同得到结果不同的很好解释 用map把列表元素变形: my @data = (4.75, 1.5, 2, 1234, 6.456, 12345, 23.94); my @formatted_data = map {&big_money($_)} @data; my @formatted_lines map的结果可以当成其他函数的参数传递 print "The money numbers are:\n"; map {sprintf("%25s\n", $_)}@formatted_data; 当然不用临时数组也是可以的; my @data = (4.75, 1.5, 2, 1234, 6.456, 12345, 23.94); 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; 更花哨的列表工具: List::Util模块包含在标准库中,它提供各式高效的常见列表处理工具,都是用c语言实现的。 简洁的: use List::Util qw(first); my $first_match = first{/\bPebbles\b/i}@characters; use List::Util qw(sum); my $total = sum(1..100); #得到总和500500 use List::Util qw(max); my $max = max(3, 5, 10, 4, 6); 字符串的话: use List::Util qw(maxstr); my $max = maxstr(@strings); 如果对列表中的元素随机排序的话,可以用shuffle实现: use List::Util qw(shuffle); my @shuffle = shuffle(1..1000); #使列表元素的排序随机 不是CPAN自带的第三方模块:List::MoreUtils; use List::MoreUtils qw(none any all); if(none {$_ > 100}@numbers) { print "No elements over 100\n"; } elsif(any {$_ > 50}@numbers ) { print "Some elements over 50\n"; } elsif(all {$_ < 10}@numbers ) { print "All elements are less than 10\n"; } 如果需要对按元素组处理列表的话,可以用natatime来取出对应位置上的元素: use List::MoreUtils qw(natatime); my $iterator = natatime 3, @array; while (my @triad = $iterator->()) { print "Got @triad\n"; } 如果要合并两个或多个列表,可以用mesh构造一个大型列表,交错填充原始列表中各个位置上的元素,就算其中某个列表长度很小都没关系: use List::MoreUtils qw(mesh); my @abc = 'a' .. 'z'; my @numbers = 1 .. 20; my @dinosaurs = qw(dino); my @large_array = mesh @abc, @numbers, @dinosaurs; =cut
不负自己