本文介紹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對進行操做有很多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
。code
再好比計算列表中數值的總和:對象
my $sum = reduce { $a + $b } 1..10;
若是@list
是空列表,則reduce返回undef,若是@list
是隻有一個元素的列表,則直接返回這個元素,不會評估block。blog
爲了編寫健壯的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是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 {BLOCK},@list;
若是@list
中全部元素都應用於BLOCK都返回true,或者@list
爲空,則all()返回true,不然返回false。
語法:
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 {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分別用來取出列表中的最大數值和最大字符串。若是列表爲空,則返回undef。
max @list maxstr @list
例如:
max qw(1 3 10 5 11 12); max @list1, @list2;
min和minstr分別用來取出列表中的最小數值和最小字符串。若是列表爲空,則返回undef。
min @list minstr @list
返回列表中全部元素的乘積。
product 3,4,5 # 60 product 1..10 # 3628800
sum和sum0都計算列表中全部元素的和,不一樣的是前者在列表爲空時返回undef,然後者在列表爲空時返回0。
以key/value成對的方式迭代列表,因此列表元素的個數須要是偶數。
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;
分別返回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";
@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
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";
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
打亂給定列表中元素的順序。
my @values = shuffle @values;
例如:
$ perl -MList::Util=shuffle -e '$,=" "; print shuffle 1..9' 4 3 9 7 6 5 8 2 1
去除列表中的重複元素。只有連續的相同的兩元素才認爲是重複的,若是有連續的重複元素,則保留第一個重複元素。
標量上下文下返回去重後的元素個數。
例如:
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。
對列表中的數值進行去重操做。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。
相似於uniqnum,只不過undef元素和空字符串被認爲是相等的,保留undef仍是空字符串的規則同uniqnum。