Perl List::Util模块用法详解

本文介绍Perl标准库List::Utils中的列表工具,有时候它们很是好用。好比Perl中测试列表中是否包含某个元素(某个元素是否存在于列表中)没有比较直接比较方便的功能,但使用List::Utils中的first或any函数,则很是方便且高效。此外,该模块都有对应的C代码的函数,因此它们的效率也不差。数组

能够将List::Utils模块中的功能大体分为3类:reduce类、key/value类以及其它类:函数

  • reduce类:reduce类的函数是依次取(迭代)列表中的元素,并对这些元素作一些操做,直到列表中元素个数减为1或0个。用法和grep/map函数相似
  • key/value类:reduce类是每次取一个元素进行操做,key/value类函数是每次取连续的两个元素做为key/value对进行操做
  • 其余类中包括打乱列表顺序的shuffle函数,去重函数uniq、uniqnum和uniqstr

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),赋值给$maxcode

再好比计算列表中数值的总和:对象

my $sum = reduce { $a + $b } 1..10;

若是@list是空列表,则reduce返回undef,若是@list是只有一个元素的列表,则直接返回这个元素,不会评估blockblog

为了编写健壮的reduce(),常用reduce的一种通用技巧:为了不列表元素数量的不足,使用一个或多个额外的不影响结果的元素压入到目标列表中。例如:排序

reduce {BLOCK} 0,@list;
reduce {BLOCK} 1,@list;
reduce {BLOCK} undef,@list;
reduce {BLOCK} undef,undef,@list;

例如,计算列表中元素的总和:three

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。

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

其它函数

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。

相关文章
相关标签/搜索