学习笔记 Perl grep函数使用揭秘

本文和大家重点讨论一下Perl grep函数的使用,Perl grep函数会根据LIST中的元素对BLOCK或EXPR做出评估,BLOCK块是一个或多个由花括号分隔开的Perl语句。而List则是一串被排序的值。

Perl grep函数的使用

关于Perl grep函数

(如果你是个Perl的新手,你可以先跳过下面的两段,直接到Grepvs.loops样例这一部分,放心,在后面你还会遇到它)

grepBLOCKLIST
grepEXPR,LIST

Perl grep函数会根据LIST中的元素对BLOCK或EXPR做出评估,而且会把局部变量$_设置为当前所用的LIST中的元素。BLOCK块是一个或多个由花括号分隔开的Perl语句。而List则是一串被排序的值。EXPR是一个或多个变量,操作符,字符,函数,子程序调用的综合体。Grep会返回一组经BLOCK或EXPR块的估值后是真的元素。如果BLOCK块由多个语句组成,那么Grep以BLOCK中的最后一条语句的估计值为准。LIST可以是一个列表也可以是一个数组。在标量上下文中,grep返回的是可以被BLOCK或EXPR估为真的元素个数。

请避免在BLOCK或EXPR块中修改$_,因为这会相应的修改LIST中的元素。同时还要避免把grep返回的列表做为左值使用,因为这也会修改LIST中的元素。(所谓左值变量就是一个在赋值表达式左边的变量)。一些Perlhackers可能会利用这个所谓的"特性",但是我建议你不要使用这种混乱的编程风格.

Perl grep函数与循环

这个例子打印出myfile这个文件中含有terriosm和nuclear的行(大小写不敏感).

openFILE"<myfile"ordie"Can'topenmyfile:$!";  



printgrep/terrorism|nuclear/i,<FILE>; 


对于文件很大的情况,这段代码耗费很多内存。因为grep把它的第二个参数作为一个列表上下文看待,所以<>操作符返回的是整个的文件。更有效的代码应该这样写:

while($line=<FILE>){  



if($line=~/terrorism|nuclear/i){print$line}  



} 

通过上面可以看到,使用循环可以完成所有grep可以完成的工作。那为什么我们还要使用grep呢?一个直观的答案是grep的风格更像Perl,而loops(循环)则是C的风格。一个更好的答案是,首先,grep很直观的告诉读者正在进行的操作是从一串值中选出想要的。其次,grep比循环简洁。(用软件工程的说法就是grep比循环更具有内聚力)。基本上,如果你对Perl不是很熟悉,随便你使用循环。否则,你应该多使用像grep这样的强大工具.

计算数组中匹配给定模式的元素个数
在一个标量上下文中,grep返回的是匹配的元素个数.

$num_apple=grep/^apple$/i,@fruits;^和$匹配符的联合使用指定了只匹配那些以apple开头且同时以apple结尾的元素。这里grep匹配apple但是pineapple就不匹配。

输出列表中的不同元素

@unique=grep{++$count{$_}<2}  


qw(abacddefgfhh);  


print"@unique\n"; 

输出结果:abcdefgh$count{$_}是Perl散列中的一个元素,是一个键值对(Perl中的散列和计算机科学中的哈希表有关系,但不完全相同)这里count散列的键就是输入列表中的各个值,而各键对应的值就是该键是否使BLOCK估值为真的次数。当一个值第一次出现的时候BLOCK的值被估为真(因为小于2),当该值再次出现的时候就会被估计为假(因为等于或大于2)。

取出列表中出现两次的值

@crops=qw(wheatcornbarleyricecornsoybeanhay  


alfalfaricehaybeetscornhay);  



@duplicates=grep{$count{$_}==2}  




grep{++$count{$_}>1}@crops;  



print"@duplicates\n"; 

在grep的第一个列表元素被传给BLOCK或EXPR块前,第二个参数被当作列表上下文看待。这意味着,第二个grep将在左边的grep开始对BLOCK进行估值之前完全读入count散列。

列出当前目录中的文本文件

@files=grep{-fand-T}glob'*.*';
print"@files\n";
glob函数是独立于操作系统的,它像Unix的shell一样对文件的扩展名进行估计。单个的*表示匹配所以当前目录下不以.开头的文件,.*表示匹配当前目录下以.开头的所有文件.如果一个文件是文本文件-f和-T文件测试符则返回真。使用-fand-T进行测试要比单用-T进行测试有效,因为如果一个文件没有通过-f测试,那么-T测试就不会进行,而-f测试比-T耗时更少.

从数组中选出元素并消除重复

@array=qw(Tobeornottobethatisthequestion);  


print"@array\n";  



@found_words=  




grep{$_=~/b|o/iand++$counts{$_}<2;}@array;  



print"@found_words\n"; 

输出结果:
Tobeornottobethatisthequestion
Tobeornottoquestio
逻辑表达式$_=~/b|o/i匹配包含有b或o的元素(区别大小写)。把匹配操作放在计数工作前要比把计数工作放在前面有效些。比如,如果左边的表达式测试失败,那么右边的表达式就不会被计算.

选出二维坐标数组中横坐标大于纵坐标的元素

#Anarrayofreferencestoanonymousarrays  



@data_points=([5,12],[20,-3],  



[2,2],[13,20]);  



@y_gt_x=grep{$_->[0]<$_->[1]}@data_points;  




foreach$xy(@y_gt_x){print"$xy->[0],$xy->[1]\n"} 

输出结果:
5,12
13,20

在数据库中查找餐馆

这个例子实现数据库的方法不适合在实际中使用的,但是它说明了使用Perl grep函数的时候,只要你的内存够用,BLOCK块的复杂度基本没有限制.

#@databaseisarrayofreferencestoanonymoushashes  



@database=(  




{name=>"WildGinger",  




city=>"Seattle",  




cuisine=>"AsianThaiChineseKoreanJapanese",  




expense=>4,  




music=>"\0",  




meals=>"lunchdinner",  




view=>"\0",  




smoking=>"\0",  




parking=>"validated",  




rating=>4,  




payment=>"MCVISAAMEX",  



},  


#{...},etc.  


);  


 


subfindRestaurants{  


my($database,$query)=@_;  


returngrep{  



$query->{city}?  




lc($query->{city})eqlc($_->{city}):1  




and$query->{cuisine}?  




$_->{cuisine}=~/$query->{cuisine}/i:1  




and$query->{min_expense}?  




$_->{expense}>=$query->{min_expense}:1  




and$query->{max_expense}?  




$_->{expense}<=$query->{max_expense}:1  




and$query->{music}?$_->{music}:1  




and$query->{music_type}?  




$_->{music}=~/$query->{music_type}/i:1  




and$query->{meals}?  




$_->{meals}=~/$query->{meals}/i:1  




and$query->{view}?$_->{view}:1  




and$query->{smoking}?$_->{smoking}:1  




and$query->{parking}?$_->{parking}:1  




and$query->{min_rating}?  




$_->{rating}>=$query->{min_rating}:1  




and$query->{max_rating}?  




$_->{rating}<=$query->{max_rating}:1  




and$query->{payment}?  




$_->{payment}=~/$query->{payment}/i:1  



}@$database;  


}  


 



%query=(city=>'Seattle',cuisine=>'Asian|Thai');  




@restaurants=findRestaurants(\@database,\%query);  




print"$restaurants[0]->{name}\n";  



 

相关推荐