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

posted @ 2015-09-21 02:11  笑面浮屠  阅读(281)  评论(0编辑  收藏  举报