Perl列表处理模块-List::Util


在Perl中有一些专门用于处理列表数据的模块,比如说List::Util模块,该模块内置于标准库中,能提供各种高效的常见列表处理工具。还有List::Util::XS模块,因其用C语言来实现,速度一般都很快。如果那其中的函数不够用,还可以引入第三方处理模块:List::MoreUtils,它能提供更多的列表处理功能。如果想全部使用这两个模块中的功能函数,可以直接引入List::AllUtils模块。关于数组列表中元素判断,可参考:Perl判断变量元素是否存在于数组列表中。测试列表中是否包含某个元素(某个元素是否存在于列表中)没有比较直接比较方便的功能,但使用List::Utils中的first或any函数,则非常方便且高效。此外,该模块都有对应的C代码的函数,所以它们的效率也不差。
List::Util - A selection of general-utility list subroutines
List::Util contains a selection of subroutines that people have expressed would be nice to have in the perl core, but the usage would not really be high enough to warrant the use of a keyword, and the size so small such that being individual extensions would be wasteful.By default List::Util does not export any subroutines.
List::Util包含在perl核心模块中,它们能够满足日常的列表处理任务。需要注意的是:默认不导入任何子例程。
可供调用的内部函数:
use List::Util qw(first max maxstr min minstr reduce shuffle sum);
可以将List::Utils模块中的功能大致分为3类:reduce类、key/value类以及其它类:
1、reduce类:reduce类的函数是依次取(迭代)列表中的元素,并对这些元素做一些操作,直到列表中元素个数减为1或0个。用法和grep/map函数类似。
2、key/value类:reduce类是每次取一个元素进行操作,key/value类函数是每次取连续的两个元素作为key/value对进行操作。
3、其他类中包括打乱列表顺序的shuffle函数,去重函数uniq、uniqnum和uniqstr。
1、reduce类函数
有不少reduce类的工具,reduce()是这些工具的通用化函数,其它函数都是reduce()的特定情况改编而来。
reduce()
$res = reduce {block} @list
reduce()函数首先从列表@list中取出两个元素赋值给$a和$b,对这两个变量应用block代码得到一个结果,这个结果赋值给$a,再从@list中取下一个元素赋值给$b,继续与$a应用block代码。直到所有元素都应用完了block代码,最后返回block的结果。
my $max = reduce { $a > $b ? $a : $b} qw(10 5 2 20 14);
上面的代码表示从@list中取出值最大的元素。上面的处理过程大概是这样的:首先从列表中取出2个元素(即10和5)赋值给$a和$b,然后执行大括号中的代码逻辑,这段代码逻辑要么返回$a,要么返回$b,它的返回值(即较大值10)再次赋值给$a,然后继续从list中取出另一个元素(即2)赋值给$b,$a和$b再次进行比较取出较大者(即10),依此类推,最终取出最大的元素(即20),赋值给$max。
再比如计算列表中数值的总和:
my $sum = reduce { $a + $b } 1..10;
如果@list是空列表,则reduce返回undef,如果@list是只有一个元素的列表,则直接返回这个元素,不会评估block。
为了编写健壮的reduce(),经常使用reduce的一种通用技巧:为了避免列表元素数量的不足,使用一个或多个额外的不影响结果的元素压入到目标列表中。例如:
reduce {BLOCK} 0,@list;
reduce {BLOCK} 1,@list;
reduce {BLOCK} undef,@list;
reduce {BLOCK} undef,undef,@list;
例如,计算列表中元素的总和:
my $sum = reduce {$a+$b} 0,@list;
any
any是reduce类的一个工具,用于判断列表中是否存在某个符合条件的元素,如果存在则立即退出。它有点像grep,但是它比grep在元素判断上更优化,因为grep总会迭代所有元素,而any在得到结果时会立即退出。
语法:any {BLOCK} @list
any()将依次迭代列表中的元素并赋值给$_,然后应用BLOCK,如果某个元素能使BLOCK返回true,则any()直接返回true并退出any。
如果所有元素都不能使得block返回true,则any返回false,或者@list为空列表则也返回false。
在Perl中没有比较直接的方式来判断某个元素是否存在于列表中,但使用any会非常简便且高效(后面介绍的first()也可以判断)。例如,判断"xyz"是否存在于数组中:
if(any {"xyz" eq $_} @list){
print qw('"xyz" exists in @list')
}
与之等价的grep()判断为:
if (grep {"xyz" eq $_} @list){
print qw('"xyz" exists in @list')
}
前面说了,grep判断的效率要低一些,不管元素是否存在,它总会迭代完列表所有元素才退出,而any在第一次判断存在的时候就退出。
all
all {BLOCK},@list;
如果@list中所有元素都应用于BLOCK都返回true,或者@list为空,则all()返回true,否则返回false。
none和notall
语法:
none {BLOCK} @list;
notall {BLOCK} @list;
none()是any()的反意,notall()是all()的反意。
none()表示如果@list中所有元素应用于BLOCK时都不返回true,则none()返回true。
notall()表示@list中并非所有元素应用于BLOCK时都返回true,则notall()返回true。如果没有元素返回True,则也返回true。所以,notall是all的反意。
first
语法:first {BLOCK} @list;
迭代list中的每个元素,返回第一个应用于BLOCK返回true的元素。注意,只要发现了一个满足条件的元素,就立即停止迭代,这和any()是类似的。如果所有元素都返回false,或者list为空,则first返回undef。
# 返回list中第一个已定义的元素
$foo = first {defined($_)} @list;
# 返回list中第一个大于某值的元素
$foo = first { $_ > 23 } @list;
# 判断list中是否存在某个元素
if (first { $_ eq "abc" } @list) {...}
max和maxstr
max和maxstr分别用来取出列表中的最大数值和最大字符串。如果列表为空,则返回undef。
max @list
maxstr @list
例如:
max qw(1 3 10 5 11 12);
max @list1, @list2;
min和minstr
min和minstr分别用来取出列表中的最小数值和最小字符串。如果列表为空,则返回undef。
min @list
minstr @list
product
返回列表中所有元素的乘积。
product 3,4,5 # 60
product 1..10 # 3628800
sum和sum0
sum和sum0都计算列表中所有元素的和,不同的是前者在列表为空时返回undef,而后者在列表为空时返回0。
2、key/value类函数
以key/value成对的方式迭代列表,所以列表元素的个数需要是偶数。
pairs和unpairs
pairs用法:
@each = pairs @list;
print "@{$each[0]}";
print "@{$each[1]}";
print "@{$each[2]}";
print "@{$each[3]}";
...
这个函数从一个列表中每次取两个元素,分别作为key/value(Perl中key/value也一样是列表)放进一个数组中,并返回这个数组的引用。所以,每次迭代时返回的数组引用中都只包含两个元素。
例如:
my @arr = qw(one 1 two 2 three 3);
my @pairs = pairs @arr;
上面的数组@pairs中包含了3个数组的引用:
$" = "\n";
print "@pairs";
结果:
List::Util::_Pair=ARRAY(0x64ad30)
List::Util::_Pair=ARRAY(0x6549a8)
List::Util::_Pair=ARRAY(0x654a20)
这里的每个数组引用所指向的数组都只包含2个元素,这两个元素来自于"@arr"中每次取出的两个元素。例如,解除每个数组引用:
print "@{$pairs[0]}"; # one 1
print "@{$pairs[1]}"; # two 2
print "@{$pairs[2]}"; # three 3
因为pairs每次都返回一个数组引用,可以去迭代pairs的结果。且每个数组引用对象都可以使用key和value方法从这个2元素的数组中取出key和value:
foreach my $pair (pairs @arr) {
print "@{$pair}";
}
foreach my $pair (pairs @arr) {
print $pair->key; # 从每个数组引用中取出key
print $pair->value; # 从每个数组引用中取出value
}
unpairs()和pairs()作用正好相反,pairs()是将一个列表中的每两个元素组成一个key/value对的数组引用,unpairs()则是将一个包含每两个元素的数组引用的数组解除成列表。它在结果上等价于:
my @arr = map { @{$_}[0,1] } @pairs;
对于qw(one 1 two 2 three 3)列表,pairs()的结果是构造下面的数组,unpairs()则是将@pairs这样的结构还原成一个list。
@pairs = ( ["one", 1], ["two", 2], ["three", 3] );
在pairs()转换和unpairs()还原的过程中间,可以加上一些函数来操作key/value对。例如:
# 类似于后面的pairgrep
my @arr = unpairs grep {FUNC} pairs @arr;
# 类似于后面的pairmap
my @arr = unpairs map {FUNC} pairs @arr;
还可以将每个key/value对使用sort进行排序。例如按照key排序:
my @arr = unpairs sort {$a->key cmp $b->key} pairs @arr;
pairkeys和pairvalues
分别返回pairs得到的key/value对的key和value列表,也就是分别取得pairs操作列表的第奇数个元素和第偶数个元素。
例如:
use List::Util qw(pairs pairkeys pairvalues);
my @arr = qw(one 1 two 2 three 3);
my @keys = pairkeys @arr; # one, two, three
print "@keys";
my @values = pairvalues @arr; # 1 2 3
print "@values";
pairgrep
@arr = pairgrep {BLOCK} @list;
$count = pairgrep {BLOCK} @list;
和Perl的grep函数类似,不同的是pairgrep筛选的对象是key/value对,每次迭代key/value对的时候,都会分别设置变量$a和$b为key和value。
在列表上下文中,它返回的是满足条件key/value对,它会直接还原成列表中的元素,所以返回的列表中包含连续的key和value。
在标量上下文中,则是返回满足条件的key/value对的数量。所以标量上下文的返回数量比列表上下文中元素的数量少一半。
具体看下面的示例。
例如,筛选列表中key为大写字母的key/value对:
use List::Util qw(pairgrep);
my @testarr = qw(ab cd EF gh AB CD ef GH);
my @upp = pairgrep { $a =~ m/^[[:upper:]]+$/ } @testarr;
print "@upp"; # EF gh AB CD
my $count = pairgrep { $a =~ m/^[[:upper:]]+$/ } @testarr;
print "$count"; # 2
pairfirst
my @pair = pairfirst {BLOCK} @arr;
my ($key, $val) = pairfirst {BLOCK} @arr;
my $found_or_not = pairfirst {BLOCK} @arr;
类似于first()函数,但是pairfirst操作的是key/value对。它和pairgrep一样,同样会设置$a和$b为每个key/value对中的Key和value。
在列表上下文,它返回list中的第一个满足条件的key/value对,它是一个2元素的数组。如果没有满足条件的则返回一个空列表。在标量上下文中,它简单地返回true/false而不是第一个key/value对的值,只要找到了就返回true。
use List::Util qw(pairgrep pairfirst);
my @testarr = qw(ab cd EF gh AB CD ef GH);
my @upp = pairfirst { $a =~ m/^[[:upper:]]+$/ } @testarr;
print "@upp";
my $found_or_not = pairfirst { $a =~ m/^[[:upper:]]+$/ } @testarr;
print "$found_or_not";
pairmap
my @list = pairmap {BLOCK} @arr;
my $count = pairmap {BLOCK} @arr;
类似于Perl的map函数,但是它操作的是key/value对。同样地,它会将每个key/value对中的key和value设置为$a和$b。
在列表上下文,它返回block对每个key/value的评估结果,在标量上下文,它直接返回对应于列表上下文列表元素的个数。
use List::Util qw(pairgrep pairfirst pairmap);
my @testarr = qw(ab cd EF gh AB CD ef GH);
# 每次迭代key/value对时,新列表中都有两个新构成的元素
my @upp = pairmap { "$a: $b","$a$b" } @testarr;
print "@upp";
# 上面的结果列表中有8个元素,所以这里返回8
my $count = pairmap { "$a: $b","$a$b" } @testarr;
print "$count";
返回结果:
ab: cd abcd EF: gh EFgh AB: CD ABCD ef: GH efGH
8
3、其它函数
shuffle
打乱给定列表中元素的顺序。
my @values = shuffle @values;
例如:
$ perl -MList::Util=shuffle -e '$,=" "; print shuffle 1..9'
4 3 9 7 6 5 8 2 1
uniq
去除列表中的重复元素。只有连续的相同的两元素才认为是重复的,如果有连续的重复元素,则保留第一个重复元素。
标量上下文下返回去重后的元素个数。
例如:
my @tsuniq = qw(ab ab AB 1 1 2 2.0 3 );
my @subset = uniq @tsuniq; # ab AB 1 2 2.0 3
my $count = uniq @tsuniq; # 6
需要注意的是,undef和空字符串是不同的,但是连续的undef被认为是相同的,所以只会保留一个undef。
uniqnum
对列表中的数值进行去重操作。1.0和1被认为是相同的数值元素。同样的,只有连续的重复元素才算是重复。同样的,在标量上下文返回的是去重后的元素个数。
对于undef元素,它与0比较是重复的元素,如果0在undef的前面,保留0,如果undef在0的前面,则保留undef。如果开启了警告信息(use warnings 'uninitialized';),会对undef给出警告。
use List::Util qw(uniqnum);
my @tsuniq = qw( 1 1 2 2.0 3 undef undef 0 );
my @subset = uniqnum @tsuniq; # 1 2 3 undef
my $count = uniqnum @tsuniq; # 4
如果是对qw(1 1 2 2.0 3 0 undef undef)或qw(1 1 2 2.0 3 undef 0 undef)进行去重,则去重时保留undef而不是0。
uniqstr
类似于uniqnum,只不过undef元素和空字符串被认为是相等的,保留undef还是空字符串的规则同uniqnum。
用List::Util(MoreUtils)简化列表处理
1.快速查找最大值,最小值
use List::Util qw(max min) ;
my $max_number = max ( 0 .. 1000 );
my $min_number = min (0 .. 1000 );
此外还有maxstr子程序能返回列表中最大的字符串,minstr子程序返回列表中最小的字符串。
use List::Util qw(maxstr minstr);
my $max_string = maxstr(qw(Fido Spot Rover));
sum-子程序所有数字求和求数组的和:不需要一个一个地累加,直接调用 sum 函数
use List::Util qw(sum);
use List::Util qw/sum/;
my $sum = sum ( 1.. 1000) ;
my @array = (10, 20, 30, 40);
my $sum = sum @array; # 得到 100
2.列表归并
$foo = reduce { $a < $b ? $a : $b } 1..10 # min
$foo = reduce { $a lt $b ? $a : $b } 'aa'..'zz' # minstr
$foo = reduce { $a + $b } 1 .. 10 # sum
$foo = reduce { $a . $b } @bar # concat
3. 判断是否有元素匹配
纯粹用Perl实现的话,找出列表中的第一个符合某项条件的元素,比找出所有符合条件的要麻烦一些。first子程序就是为了解决这样的问题而设计的,并且会告诉第一个元素是什么。一旦找到便停止并返回。
$foo = first { defined($_) } @list# first defined value in @list
$foo = first { $_ > $value } @list# first value in @list which
# is greater than $value
Similar to grep in that it evaluates BLOCK setting $_ to each element of @list in turn. first returns the first element where the result from BLOCK is a true value. If BLOCK never returns true or @list was empty then undef is returned.
4.一次遍历多个列表
@a = (1 .. 5);
@b = (11 .. 15);
@x = pairwise { $a + $b } @a, @b; # returns 12, 14, 16, 18, 20
# mesh with pairwise
@a = qw/a b c/;
@b = qw/1 2 3/;
@x = pairwise { ($a, $b) } @a, @b; # returns a, 1, b, 2, c, 3
对于三个及以上的列表可选用each_array子程序
my $ea = each_array(@a, @b, @c);
while ( my ($a, $b, $c) = $ea->() ) { .... }
5.数组合并
@x = qw/a b c d/;
@y = qw/1 2 3 4/;
@z = mesh @x, @y; # returns a, 1, b, 2, c, 3, d, 4
@a = ('x');
@b = ('1', '2');
@c = qw/zip zap zot/;
@d = mesh @a, @b, @c; # x, 1, zip, undef, 2, zap, undef, undef, zot
---------------------
Perl List::Util模块使用实例
例01、扫描符合条件的某个列表,并取出第一个符合条件的
常规做法:
use 5.010;
my @names = qw(Wendy Jerry Betty Wendy Alice);
foreach (@names) {
if(/\bWendy\b/i){
$match = $_;
last;
}
}
say $match;
如果改用List::Util模块提供的first子程序,就要简单的多,代码如下:
use List::Util qw(first);
my $match = first {/\bWendy\b/i} @names; #找到第一个Wendy即终止
例02、求1到1000之间的和
常规做法:
代码如下:
use 5.010;
my $total = 0;
foreach (1..1000) {
$total += $_;
}
say $total; #结果500500
如果改用List::Util模块提供的sum子程序,同样很简单,代码如下:
use List::Util qw(sum);
my $total = sum(1..1000); #结果500500
例03、求一组数字的最大值与最小值
常规做法,代码如下:
use utf8;
sub max{
my($max_so_far) = shift @_; #数组中第一个值,暂时当成最大值。
foreach(@_){#遍历数组@_
if($_>$max_so_far){ #看其它元素是否有比$max_so_far大的值。
$max_so_far = $_;} #如果有话,更新最大值变量
}
$max_so_far;
}
my $_MaxData = &max(2,3,8,5,10);
print $_MaxData; #结果为10
如果改用List::Util模块提供的max子程序,则非常简单,代码如下:
use List::Util qw(max);
my $max = max(2, 3, 8, 5, 10);
print $max; #结果为10
同样道理,使用List::Util模块提供的min子程序,可求最小值,代码如下:
use List::Util qw(min);
my $min = min(2, 3, 8, 5, 10); #最小值为2
例04、对一组字符串进行排序
如果用常规方法的话,必须按顺序一个个进行比较,用List::Util的maxstr子程序可以轻松实现,代码如下:
use List::Util qw(maxstr);
my $max_str = maxstr( qw/Jerry Betty Alice Fred Barney jerry/ );
print $max_str;
例05、对列表中的元素随机排序
如果用常规方法的话,很难实现,而用List::Util中的shuffle子程序,则非常简单,一条命令搞定!代码如下:
use List::Util qw(shuffle);
my @shuffled_nums = shuffle(1..10); # 3 9 8 5 6 4 1 10 2 7
my @shuffled_name = shuffle('A'..'G');# F E G A B D
例06、检查列表中是否没有某个元素,或者有任何元素,或者所有元素都符合条件。支持类似grep语法,如果用常规方法的话,很难实现,而用List::MoreUtils同样很容易实现,代码如下:
use List::MoreUtils qw(none any all);
my @numbers = qw(7 4 1 3 78);
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 < 10\n";}
NOTE: List::MoreUtils非内置的模块,需要下载。
例07、同时处理多个姓名列表,每次取出2位,代码如下:
use List::MoreUtils qw(natatime);
my @names_1 = qw(Alice Bob Carly);
my @names_2 = qw(David Edward Foo);
my $names = natatime(2, @names_1, @names_2); #natatim (N at a time:同时处理N组)有多个列表的话往里面放就好了
while (my @name = $names->()) { #遍历,方便后面输出
print "Got @name\n";
}
#输出结果
Got Alice Bob
Got Carly David
Got Edward Foo
例08、合并多个列表为一个列表,代码如下:
use List::MoreUtils qw(mesh);
my @array_1 = 'A' .. 'D';
my @array_2 = 1 .. 4;
my @array_3 = qw( jerry alice wendy );
my @array_new = mesh(@array_1, @array_2, @array_3);
print @array_new;
#输出结果:
A=>1=>jerry=> B=>2=>alice=> C=>3=>wendy=> D=>4
第1次: 第一个列表中取A,第二个列表中取出1,第三个列表中取出jerry
第2次: 第一个列表中取B, 第二个列表中取出2,第三个列表中取出alice
......
依次类推!
例09、往指定的字符串中加字符
可以用List::MoreUtils中的insert_after子程序,代码如下:
use v5.10;
use List::MoreUtils qw(:all);
my @list = qw/This is a list/;
insert_after {$_ eq 'a'} "longer" => @list;
print @list; #This is a longer list
例10、对两个列表进行操作,第一个列表平方后,和第二个列表求和
可以用List::MoreUtils中的pairwise子程序,代码如下:
use v5.10;
use List::MoreUtils qw(:all);
@m = (1..4);
@n = (100..103);
@x = pairwise { ($a*$a) + $b } @m, @n; #101 105 111 119
总结
如果上面的方式用普通的方法实现,需要多次循环遍历,很麻烦,而将复杂的算法,数据结构用C来实现并封装在List模块中,则可以实现很多看似复杂的功能,从中也可以看出Perl的强大之处在于CPAN,有众多的模块支持!
参考来源:
List::Util
List::Pairwise
List::Util - A selection of general-utility list subroutines
List::Util contains a selection of subroutines that people have expressed would be nice to have in the perl core, but the usage would not really be high enough to warrant the use of a keyword, and the size so small such that being individual extensions would be wasteful.By default List::Util does not export any subroutines.
List::Util包含在perl核心模块中,它们能够满足日常的列表处理任务。需要注意的是:默认不导入任何子例程。
可供调用的内部函数:
use List::Util qw(first max maxstr min minstr reduce shuffle sum);
可以将List::Utils模块中的功能大致分为3类:reduce类、key/value类以及其它类:
1、reduce类:reduce类的函数是依次取(迭代)列表中的元素,并对这些元素做一些操作,直到列表中元素个数减为1或0个。用法和grep/map函数类似。
2、key/value类:reduce类是每次取一个元素进行操作,key/value类函数是每次取连续的两个元素作为key/value对进行操作。
3、其他类中包括打乱列表顺序的shuffle函数,去重函数uniq、uniqnum和uniqstr。
1、reduce类函数
有不少reduce类的工具,reduce()是这些工具的通用化函数,其它函数都是reduce()的特定情况改编而来。
reduce()
$res = reduce {block} @list
reduce()函数首先从列表@list中取出两个元素赋值给$a和$b,对这两个变量应用block代码得到一个结果,这个结果赋值给$a,再从@list中取下一个元素赋值给$b,继续与$a应用block代码。直到所有元素都应用完了block代码,最后返回block的结果。
my $max = reduce { $a > $b ? $a : $b} qw(10 5 2 20 14);
上面的代码表示从@list中取出值最大的元素。上面的处理过程大概是这样的:首先从列表中取出2个元素(即10和5)赋值给$a和$b,然后执行大括号中的代码逻辑,这段代码逻辑要么返回$a,要么返回$b,它的返回值(即较大值10)再次赋值给$a,然后继续从list中取出另一个元素(即2)赋值给$b,$a和$b再次进行比较取出较大者(即10),依此类推,最终取出最大的元素(即20),赋值给$max。
再比如计算列表中数值的总和:
my $sum = reduce { $a + $b } 1..10;
如果@list是空列表,则reduce返回undef,如果@list是只有一个元素的列表,则直接返回这个元素,不会评估block。
为了编写健壮的reduce(),经常使用reduce的一种通用技巧:为了避免列表元素数量的不足,使用一个或多个额外的不影响结果的元素压入到目标列表中。例如:
reduce {BLOCK} 0,@list;
reduce {BLOCK} 1,@list;
reduce {BLOCK} undef,@list;
reduce {BLOCK} undef,undef,@list;
例如,计算列表中元素的总和:
my $sum = reduce {$a+$b} 0,@list;
any
any是reduce类的一个工具,用于判断列表中是否存在某个符合条件的元素,如果存在则立即退出。它有点像grep,但是它比grep在元素判断上更优化,因为grep总会迭代所有元素,而any在得到结果时会立即退出。
语法:any {BLOCK} @list
any()将依次迭代列表中的元素并赋值给$_,然后应用BLOCK,如果某个元素能使BLOCK返回true,则any()直接返回true并退出any。
如果所有元素都不能使得block返回true,则any返回false,或者@list为空列表则也返回false。
在Perl中没有比较直接的方式来判断某个元素是否存在于列表中,但使用any会非常简便且高效(后面介绍的first()也可以判断)。例如,判断"xyz"是否存在于数组中:
if(any {"xyz" eq $_} @list){
print qw('"xyz" exists in @list')
}
与之等价的grep()判断为:
if (grep {"xyz" eq $_} @list){
print qw('"xyz" exists in @list')
}
前面说了,grep判断的效率要低一些,不管元素是否存在,它总会迭代完列表所有元素才退出,而any在第一次判断存在的时候就退出。
all
all {BLOCK},@list;
如果@list中所有元素都应用于BLOCK都返回true,或者@list为空,则all()返回true,否则返回false。
none和notall
语法:
none {BLOCK} @list;
notall {BLOCK} @list;
none()是any()的反意,notall()是all()的反意。
none()表示如果@list中所有元素应用于BLOCK时都不返回true,则none()返回true。
notall()表示@list中并非所有元素应用于BLOCK时都返回true,则notall()返回true。如果没有元素返回True,则也返回true。所以,notall是all的反意。
first
语法:first {BLOCK} @list;
迭代list中的每个元素,返回第一个应用于BLOCK返回true的元素。注意,只要发现了一个满足条件的元素,就立即停止迭代,这和any()是类似的。如果所有元素都返回false,或者list为空,则first返回undef。
# 返回list中第一个已定义的元素
$foo = first {defined($_)} @list;
# 返回list中第一个大于某值的元素
$foo = first { $_ > 23 } @list;
# 判断list中是否存在某个元素
if (first { $_ eq "abc" } @list) {...}
max和maxstr
max和maxstr分别用来取出列表中的最大数值和最大字符串。如果列表为空,则返回undef。
max @list
maxstr @list
例如:
max qw(1 3 10 5 11 12);
max @list1, @list2;
min和minstr
min和minstr分别用来取出列表中的最小数值和最小字符串。如果列表为空,则返回undef。
min @list
minstr @list
product
返回列表中所有元素的乘积。
product 3,4,5 # 60
product 1..10 # 3628800
sum和sum0
sum和sum0都计算列表中所有元素的和,不同的是前者在列表为空时返回undef,而后者在列表为空时返回0。
2、key/value类函数
以key/value成对的方式迭代列表,所以列表元素的个数需要是偶数。
pairs和unpairs
pairs用法:
@each = pairs @list;
print "@{$each[0]}";
print "@{$each[1]}";
print "@{$each[2]}";
print "@{$each[3]}";
...
这个函数从一个列表中每次取两个元素,分别作为key/value(Perl中key/value也一样是列表)放进一个数组中,并返回这个数组的引用。所以,每次迭代时返回的数组引用中都只包含两个元素。
例如:
my @arr = qw(one 1 two 2 three 3);
my @pairs = pairs @arr;
上面的数组@pairs中包含了3个数组的引用:
$" = "\n";
print "@pairs";
结果:
List::Util::_Pair=ARRAY(0x64ad30)
List::Util::_Pair=ARRAY(0x6549a8)
List::Util::_Pair=ARRAY(0x654a20)
这里的每个数组引用所指向的数组都只包含2个元素,这两个元素来自于"@arr"中每次取出的两个元素。例如,解除每个数组引用:
print "@{$pairs[0]}"; # one 1
print "@{$pairs[1]}"; # two 2
print "@{$pairs[2]}"; # three 3
因为pairs每次都返回一个数组引用,可以去迭代pairs的结果。且每个数组引用对象都可以使用key和value方法从这个2元素的数组中取出key和value:
foreach my $pair (pairs @arr) {
print "@{$pair}";
}
foreach my $pair (pairs @arr) {
print $pair->key; # 从每个数组引用中取出key
print $pair->value; # 从每个数组引用中取出value
}
unpairs()和pairs()作用正好相反,pairs()是将一个列表中的每两个元素组成一个key/value对的数组引用,unpairs()则是将一个包含每两个元素的数组引用的数组解除成列表。它在结果上等价于:
my @arr = map { @{$_}[0,1] } @pairs;
对于qw(one 1 two 2 three 3)列表,pairs()的结果是构造下面的数组,unpairs()则是将@pairs这样的结构还原成一个list。
@pairs = ( ["one", 1], ["two", 2], ["three", 3] );
在pairs()转换和unpairs()还原的过程中间,可以加上一些函数来操作key/value对。例如:
# 类似于后面的pairgrep
my @arr = unpairs grep {FUNC} pairs @arr;
# 类似于后面的pairmap
my @arr = unpairs map {FUNC} pairs @arr;
还可以将每个key/value对使用sort进行排序。例如按照key排序:
my @arr = unpairs sort {$a->key cmp $b->key} pairs @arr;
pairkeys和pairvalues
分别返回pairs得到的key/value对的key和value列表,也就是分别取得pairs操作列表的第奇数个元素和第偶数个元素。
例如:
use List::Util qw(pairs pairkeys pairvalues);
my @arr = qw(one 1 two 2 three 3);
my @keys = pairkeys @arr; # one, two, three
print "@keys";
my @values = pairvalues @arr; # 1 2 3
print "@values";
pairgrep
@arr = pairgrep {BLOCK} @list;
$count = pairgrep {BLOCK} @list;
和Perl的grep函数类似,不同的是pairgrep筛选的对象是key/value对,每次迭代key/value对的时候,都会分别设置变量$a和$b为key和value。
在列表上下文中,它返回的是满足条件key/value对,它会直接还原成列表中的元素,所以返回的列表中包含连续的key和value。
在标量上下文中,则是返回满足条件的key/value对的数量。所以标量上下文的返回数量比列表上下文中元素的数量少一半。
具体看下面的示例。
例如,筛选列表中key为大写字母的key/value对:
use List::Util qw(pairgrep);
my @testarr = qw(ab cd EF gh AB CD ef GH);
my @upp = pairgrep { $a =~ m/^[[:upper:]]+$/ } @testarr;
print "@upp"; # EF gh AB CD
my $count = pairgrep { $a =~ m/^[[:upper:]]+$/ } @testarr;
print "$count"; # 2
pairfirst
my @pair = pairfirst {BLOCK} @arr;
my ($key, $val) = pairfirst {BLOCK} @arr;
my $found_or_not = pairfirst {BLOCK} @arr;
类似于first()函数,但是pairfirst操作的是key/value对。它和pairgrep一样,同样会设置$a和$b为每个key/value对中的Key和value。
在列表上下文,它返回list中的第一个满足条件的key/value对,它是一个2元素的数组。如果没有满足条件的则返回一个空列表。在标量上下文中,它简单地返回true/false而不是第一个key/value对的值,只要找到了就返回true。
use List::Util qw(pairgrep pairfirst);
my @testarr = qw(ab cd EF gh AB CD ef GH);
my @upp = pairfirst { $a =~ m/^[[:upper:]]+$/ } @testarr;
print "@upp";
my $found_or_not = pairfirst { $a =~ m/^[[:upper:]]+$/ } @testarr;
print "$found_or_not";
pairmap
my @list = pairmap {BLOCK} @arr;
my $count = pairmap {BLOCK} @arr;
类似于Perl的map函数,但是它操作的是key/value对。同样地,它会将每个key/value对中的key和value设置为$a和$b。
在列表上下文,它返回block对每个key/value的评估结果,在标量上下文,它直接返回对应于列表上下文列表元素的个数。
use List::Util qw(pairgrep pairfirst pairmap);
my @testarr = qw(ab cd EF gh AB CD ef GH);
# 每次迭代key/value对时,新列表中都有两个新构成的元素
my @upp = pairmap { "$a: $b","$a$b" } @testarr;
print "@upp";
# 上面的结果列表中有8个元素,所以这里返回8
my $count = pairmap { "$a: $b","$a$b" } @testarr;
print "$count";
返回结果:
ab: cd abcd EF: gh EFgh AB: CD ABCD ef: GH efGH
8
3、其它函数
shuffle
打乱给定列表中元素的顺序。
my @values = shuffle @values;
例如:
$ perl -MList::Util=shuffle -e '$,=" "; print shuffle 1..9'
4 3 9 7 6 5 8 2 1
uniq
去除列表中的重复元素。只有连续的相同的两元素才认为是重复的,如果有连续的重复元素,则保留第一个重复元素。
标量上下文下返回去重后的元素个数。
例如:
my @tsuniq = qw(ab ab AB 1 1 2 2.0 3 );
my @subset = uniq @tsuniq; # ab AB 1 2 2.0 3
my $count = uniq @tsuniq; # 6
需要注意的是,undef和空字符串是不同的,但是连续的undef被认为是相同的,所以只会保留一个undef。
uniqnum
对列表中的数值进行去重操作。1.0和1被认为是相同的数值元素。同样的,只有连续的重复元素才算是重复。同样的,在标量上下文返回的是去重后的元素个数。
对于undef元素,它与0比较是重复的元素,如果0在undef的前面,保留0,如果undef在0的前面,则保留undef。如果开启了警告信息(use warnings 'uninitialized';),会对undef给出警告。
use List::Util qw(uniqnum);
my @tsuniq = qw( 1 1 2 2.0 3 undef undef 0 );
my @subset = uniqnum @tsuniq; # 1 2 3 undef
my $count = uniqnum @tsuniq; # 4
如果是对qw(1 1 2 2.0 3 0 undef undef)或qw(1 1 2 2.0 3 undef 0 undef)进行去重,则去重时保留undef而不是0。
uniqstr
类似于uniqnum,只不过undef元素和空字符串被认为是相等的,保留undef还是空字符串的规则同uniqnum。
用List::Util(MoreUtils)简化列表处理
1.快速查找最大值,最小值
use List::Util qw(max min) ;
my $max_number = max ( 0 .. 1000 );
my $min_number = min (0 .. 1000 );
此外还有maxstr子程序能返回列表中最大的字符串,minstr子程序返回列表中最小的字符串。
use List::Util qw(maxstr minstr);
my $max_string = maxstr(qw(Fido Spot Rover));
sum-子程序所有数字求和求数组的和:不需要一个一个地累加,直接调用 sum 函数
use List::Util qw(sum);
use List::Util qw/sum/;
my $sum = sum ( 1.. 1000) ;
my @array = (10, 20, 30, 40);
my $sum = sum @array; # 得到 100
2.列表归并
$foo = reduce { $a < $b ? $a : $b } 1..10 # min
$foo = reduce { $a lt $b ? $a : $b } 'aa'..'zz' # minstr
$foo = reduce { $a + $b } 1 .. 10 # sum
$foo = reduce { $a . $b } @bar # concat
3. 判断是否有元素匹配
纯粹用Perl实现的话,找出列表中的第一个符合某项条件的元素,比找出所有符合条件的要麻烦一些。first子程序就是为了解决这样的问题而设计的,并且会告诉第一个元素是什么。一旦找到便停止并返回。
$foo = first { defined($_) } @list# first defined value in @list
$foo = first { $_ > $value } @list# first value in @list which
# is greater than $value
Similar to grep in that it evaluates BLOCK setting $_ to each element of @list in turn. first returns the first element where the result from BLOCK is a true value. If BLOCK never returns true or @list was empty then undef is returned.
4.一次遍历多个列表
@a = (1 .. 5);
@b = (11 .. 15);
@x = pairwise { $a + $b } @a, @b; # returns 12, 14, 16, 18, 20
# mesh with pairwise
@a = qw/a b c/;
@b = qw/1 2 3/;
@x = pairwise { ($a, $b) } @a, @b; # returns a, 1, b, 2, c, 3
对于三个及以上的列表可选用each_array子程序
my $ea = each_array(@a, @b, @c);
while ( my ($a, $b, $c) = $ea->() ) { .... }
5.数组合并
@x = qw/a b c d/;
@y = qw/1 2 3 4/;
@z = mesh @x, @y; # returns a, 1, b, 2, c, 3, d, 4
@a = ('x');
@b = ('1', '2');
@c = qw/zip zap zot/;
@d = mesh @a, @b, @c; # x, 1, zip, undef, 2, zap, undef, undef, zot
---------------------
Perl List::Util模块使用实例
例01、扫描符合条件的某个列表,并取出第一个符合条件的
常规做法:
use 5.010;
my @names = qw(Wendy Jerry Betty Wendy Alice);
foreach (@names) {
if(/\bWendy\b/i){
$match = $_;
last;
}
}
say $match;
如果改用List::Util模块提供的first子程序,就要简单的多,代码如下:
use List::Util qw(first);
my $match = first {/\bWendy\b/i} @names; #找到第一个Wendy即终止
例02、求1到1000之间的和
常规做法:
代码如下:
use 5.010;
my $total = 0;
foreach (1..1000) {
$total += $_;
}
say $total; #结果500500
如果改用List::Util模块提供的sum子程序,同样很简单,代码如下:
use List::Util qw(sum);
my $total = sum(1..1000); #结果500500
例03、求一组数字的最大值与最小值
常规做法,代码如下:
use utf8;
sub max{
my($max_so_far) = shift @_; #数组中第一个值,暂时当成最大值。
foreach(@_){#遍历数组@_
if($_>$max_so_far){ #看其它元素是否有比$max_so_far大的值。
$max_so_far = $_;} #如果有话,更新最大值变量
}
$max_so_far;
}
my $_MaxData = &max(2,3,8,5,10);
print $_MaxData; #结果为10
如果改用List::Util模块提供的max子程序,则非常简单,代码如下:
use List::Util qw(max);
my $max = max(2, 3, 8, 5, 10);
print $max; #结果为10
同样道理,使用List::Util模块提供的min子程序,可求最小值,代码如下:
use List::Util qw(min);
my $min = min(2, 3, 8, 5, 10); #最小值为2
例04、对一组字符串进行排序
如果用常规方法的话,必须按顺序一个个进行比较,用List::Util的maxstr子程序可以轻松实现,代码如下:
use List::Util qw(maxstr);
my $max_str = maxstr( qw/Jerry Betty Alice Fred Barney jerry/ );
print $max_str;
例05、对列表中的元素随机排序
如果用常规方法的话,很难实现,而用List::Util中的shuffle子程序,则非常简单,一条命令搞定!代码如下:
use List::Util qw(shuffle);
my @shuffled_nums = shuffle(1..10); # 3 9 8 5 6 4 1 10 2 7
my @shuffled_name = shuffle('A'..'G');# F E G A B D
例06、检查列表中是否没有某个元素,或者有任何元素,或者所有元素都符合条件。支持类似grep语法,如果用常规方法的话,很难实现,而用List::MoreUtils同样很容易实现,代码如下:
use List::MoreUtils qw(none any all);
my @numbers = qw(7 4 1 3 78);
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 < 10\n";}
NOTE: List::MoreUtils非内置的模块,需要下载。
例07、同时处理多个姓名列表,每次取出2位,代码如下:
use List::MoreUtils qw(natatime);
my @names_1 = qw(Alice Bob Carly);
my @names_2 = qw(David Edward Foo);
my $names = natatime(2, @names_1, @names_2); #natatim (N at a time:同时处理N组)有多个列表的话往里面放就好了
while (my @name = $names->()) { #遍历,方便后面输出
print "Got @name\n";
}
#输出结果
Got Alice Bob
Got Carly David
Got Edward Foo
例08、合并多个列表为一个列表,代码如下:
use List::MoreUtils qw(mesh);
my @array_1 = 'A' .. 'D';
my @array_2 = 1 .. 4;
my @array_3 = qw( jerry alice wendy );
my @array_new = mesh(@array_1, @array_2, @array_3);
print @array_new;
#输出结果:
A=>1=>jerry=> B=>2=>alice=> C=>3=>wendy=> D=>4
第1次: 第一个列表中取A,第二个列表中取出1,第三个列表中取出jerry
第2次: 第一个列表中取B, 第二个列表中取出2,第三个列表中取出alice
......
依次类推!
例09、往指定的字符串中加字符
可以用List::MoreUtils中的insert_after子程序,代码如下:
use v5.10;
use List::MoreUtils qw(:all);
my @list = qw/This is a list/;
insert_after {$_ eq 'a'} "longer" => @list;
print @list; #This is a longer list
例10、对两个列表进行操作,第一个列表平方后,和第二个列表求和
可以用List::MoreUtils中的pairwise子程序,代码如下:
use v5.10;
use List::MoreUtils qw(:all);
@m = (1..4);
@n = (100..103);
@x = pairwise { ($a*$a) + $b } @m, @n; #101 105 111 119
总结
如果上面的方式用普通的方法实现,需要多次循环遍历,很麻烦,而将复杂的算法,数据结构用C来实现并封装在List模块中,则可以实现很多看似复杂的功能,从中也可以看出Perl的强大之处在于CPAN,有众多的模块支持!
参考来源:
List::Util
List::Pairwise