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。

相關文章
相關標籤/搜索