List::Util - 一系列通用清單子常式
use List::Util qw(
reduce any all none notall first reductions
max maxstr min minstr product sum sum0
pairs unpairs pairkeys pairvalues pairfirst pairgrep pairmap
shuffle uniq uniqint uniqnum uniqstr zip mesh
);
List::Util
包含一系列子常式,人們表示很希望 perl 核心有這些子常式,但使用率並不足以保證使用關鍵字,而且大小很小,如果作為個別擴充功能會很浪費。
預設情況下,List::Util
沒有匯出任何子常式。
以下函數集會將給定的程式碼區塊套用至值清單。
$result = reduce { BLOCK } @list
透過多次在純量內容中呼叫 BLOCK
,設定 $a
和 $b
,來簡化 @list
。第一次呼叫會將 $a
和 $b
設定為清單中的前兩個元素,後續呼叫會透過將 $a
設定為前一次呼叫的結果,並將 $b
設定為清單中的下一個元素來執行。
傳回對 BLOCK
的最後一次呼叫結果。如果 @list
為空,則傳回 undef
。如果 @list
僅包含一個元素,則傳回該元素,且不會執行 BLOCK
。
以下範例皆說明如何使用 reduce
來實作此模組中的其他清單簡化函數。(事實上它們並非以這種方式實作,而是以個別 C 函數中更有效率的方式實作)。
$foo = reduce { defined($a) ? $a :
$code->(local $_ = $b) ? $b :
undef } undef, @list # first
$foo = reduce { $a > $b ? $a : $b } 1..10 # max
$foo = reduce { $a gt $b ? $a : $b } 'A'..'Z' # maxstr
$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
$foo = reduce { $a || $code->(local $_ = $b) } 0, @bar # any
$foo = reduce { $a && $code->(local $_ = $b) } 1, @bar # all
$foo = reduce { $a && !$code->(local $_ = $b) } 1, @bar # none
$foo = reduce { $a || !$code->(local $_ = $b) } 0, @bar # notall
# Note that these implementations do not fully short-circuit
如果演算法需要 reduce
產生身分值,請務必始終將該身分值傳遞為第一個引數,以防止傳回 undef
$foo = reduce { $a + $b } 0, @values; # sum with 0 identity value
上述範例程式碼區塊也建議如何使用 reduce
來建立這些基本函數之一與 map
區塊的更有效率的組合版本。例如,若要找出清單中所有字串的總長度,我們可以使用
$total = sum map { length } @strings;
然而,這會產生一個暫時整數值清單,長度與原始字串清單相同,只不過是再次簡化為單一值。我們可以使用 reduce
搭配程式碼區塊來更有效率地計算相同結果,累積長度,改寫為
$total = reduce { $a + length $b } 0, @strings
其他傳回純量的清單簡化函數都是此一般概念的特殊化。
@results = reductions { BLOCK } @list
自版本 1.54 起。
類似於 reduce
,但它也會傳回中間值以及最終結果。與之前相同,$a
會設定為給定清單的第一個元素,然後 BLOCK
會針對設定在 $b
中的清單中剩餘項目呼叫一次,結果會擷取為回傳值,並成為 $a
的新值。
傳回的清單會從 $a
的初始值開始,接著是區塊中的每個回傳值,依序排列。結果的最終值會與 reduce
函數在給定相同區塊和清單的情況下傳回的值相同。
reduce { "$a-$b" } "a".."d" # "a-b-c-d"
reductions { "$a-$b" } "a".."d" # "a", "a-b", "a-b-c", "a-b-c-d"
my $bool = any { BLOCK } @list;
自版本 1.33 起。
類似於 grep
,它會評估 BLOCK
,並將 $_
設定為 @list
中每個元素。any
會傳回 true,如果任何元素讓 BLOCK
傳回 true 值。如果 BLOCK
從未傳回 true 或 @list
為空,則傳回 false。
許多在條件式中使用 grep
的情況,可以使用 any
取代,因為它可以在第一個 true 結果後短路。
if( any { length > 10 } @strings ) {
# at least one string has more than 10 characters
}
注意:由於 XS 問題,傳遞的區塊可能會直接存取外部 @_。這並非預期,且會在偵錯器中中斷。
my $bool = all { BLOCK } @list;
自版本 1.33 起。
類似於 "any",但它需要 @list
中的所有元素都讓 BLOCK
傳回 true。如果任何元素傳回 false,則傳回 false。如果 BLOCK
從未傳回 false 或 @list
為空,則傳回 true。
注意:由於 XS 問題,傳遞的區塊可能會直接存取外部 @_。這並非預期,且會在偵錯器中中斷。
my $bool = none { BLOCK } @list;
my $bool = notall { BLOCK } @list;
自版本 1.33 起。
類似於 "any" 和 "all",但傳回的意義相反。none
僅在 @list
中沒有值讓 BLOCK
傳回 true 時傳回 true,而 notall
僅在並非所有值都這樣做時傳回 true。
注意:由於 XS 問題,傳遞的區塊可能會直接存取外部 @_。這並非預期,且會在偵錯器中中斷。
my $val = first { BLOCK } @list;
類似於 grep
,它會評估 BLOCK
,並將 $_
設定為 @list
中每個元素。first
傳回第一個元素,其中 BLOCK
的結果為 true 值。如果 BLOCK
從未傳回 true 或 @list
為空,則傳回 undef
。
$foo = first { defined($_) } @list # first defined value in @list
$foo = first { $_ > $value } @list # first value in @list which
# is greater than $value
my $num = max @list;
傳回清單中數值最高的項目。如果清單為空,則傳回 undef
。
$foo = max 1..10 # 10
$foo = max 3,9,12 # 12
$foo = max @bar, @baz # whatever
my $str = maxstr @list;
類似於 "max",但將清單中的所有項目視為字串,並傳回由 gt
算子定義的最高字串。如果清單為空,則傳回 undef
。
$foo = maxstr 'A'..'Z' # 'Z'
$foo = maxstr "hello","world" # "world"
$foo = maxstr @bar, @baz # whatever
my $num = min @list;
類似於 "max",但傳回清單中數值最低的項目。如果清單為空,則傳回 undef
。
$foo = min 1..10 # 1
$foo = min 3,9,12 # 3
$foo = min @bar, @baz # whatever
my $str = minstr @list;
類似於 "min",但將清單中的所有項目視為字串,並根據 lt
算子定義的內容傳回字串中最小的。如果清單為空,則傳回 undef
。
$foo = minstr 'A'..'Z' # 'A'
$foo = minstr "hello","world" # "hello"
$foo = minstr @bar, @baz # whatever
my $num = product @list;
自版本 1.35 起。
傳回 @list
中所有元素的數值乘積。如果 @list
為空,則傳回 1
。
$foo = product 1..10 # 3628800
$foo = product 3,9,12 # 324
my $num_or_undef = sum @list;
傳回 @list
中所有元素的數值總和。為了向後相容,如果 @list
為空,則傳回 undef
。
$foo = sum 1..10 # 55
$foo = sum 3,9,12 # 24
$foo = sum @bar, @baz # whatever
my $num = sum0 @list;
自版本 1.26 起。
類似於 "sum",但如果給定空清單,則傳回 0,而不是 undef
。
以下一組函數皆受到 List::Pairwise 啟發,會使用一組成對的偶數大小清單。這些成對項目可能是來自雜湊的 key/value 關聯,或只是值清單。這些函數皆會保留成對項目的原始排序,而且不會因為多個成對項目具有相同的「key」值而感到混淆,甚至不需要每對項目的第一個項目都是純字串。
注意:在撰寫本文時,以下會使用區塊的 pair*
函數不會修改區塊內的 $_
值,而是使用 $a
和 $b
全域變數進行運算。這項設計很糟糕,因為它排除了提供 pairsort
函數的能力。更好的做法是傳遞類似於 pairs
函數傳回值的 2 元素陣列參考作為 $_
中的成對物件。在未來的版本中可能會新增此行為。
在此之前,會提醒使用者不要依賴控制區塊內外 $_
值保持不變。特別是,以下範例是不安全的
my @kvlist = ...
foreach (qw( some keys here )) {
my @items = pairgrep { $a eq $_ } @kvlist;
...
}
請改用詞彙變數撰寫此範例
foreach my $key (qw( some keys here )) {
my @items = pairgrep { $a eq $key } @kvlist;
...
}
my @pairs = pairs @kvlist;
自版本 1.29 起。
這是操作偶數大小成對清單的便捷捷徑,此函數會傳回 ARRAY
參考清單,每個參考包含給定清單中的兩個項目。這是更有效率的版本
@pairs = pairmap { [ $a, $b ] } @kvlist
在 foreach
迴圈中使用最為方便,例如
foreach my $pair ( pairs @kvlist ) {
my ( $key, $value ) = @$pair;
...
}
自版本 1.39
起,這些 ARRAY
參考是受祝福的物件,會辨識 key
和 value
這兩個方法。以下程式碼是等效的
foreach my $pair ( pairs @kvlist ) {
my $key = $pair->key;
my $value = $pair->value;
...
}
自版本 1.51
起,它們也有一個 TO_JSON
方法,可簡化序列化。
my @kvlist = unpairs @pairs
自版本 1.42 起。
pairs
的反函數;此函數會使用一組包含兩個元素的 ARRAY
參考清單,並傳回一個扁平清單,其中包含每個成對項目的兩個值,依序排列。這在概念上等同於
my @kvlist = map { @{$_}[0,1] } @pairs
除了在內部更有效率地實作。具體來說,對於任何輸入項目,它會從輸出清單中提取兩個值;如果輸入陣列參考很短,則使用 undef
。
在 pairs
和 unpairs
之間,可以使用高階清單函式對配對進行操作,就像單一純量一樣;例如以下近似等效的其他 pair*
高階函式
@kvlist = unpairs grep { FUNC } pairs @kvlist
# Like pairgrep, but takes $_ instead of $a and $b
@kvlist = unpairs map { FUNC } pairs @kvlist
# Like pairmap, but takes $_ instead of $a and $b
不過請注意,這些版本在純量語境中不會表現得很好。
最後,此技術可用於對 keyvalue 配對清單進行排序;例如
@kvlist = unpairs sort { $a->key cmp $b->key } pairs @kvlist
my @keys = pairkeys @kvlist;
自版本 1.29 起。
這是對偶數大小的配對清單進行操作的便捷捷徑,此函式會傳回給定清單中每個配對的第一個值清單。這是更有效率的版本
@keys = pairmap { $a } @kvlist
my @values = pairvalues @kvlist;
自版本 1.29 起。
這是對偶數大小的配對清單進行操作的便捷捷徑,此函式會傳回給定清單中每個配對的第二個值清單。這是更有效率的版本
@values = pairmap { $b } @kvlist
my @kvlist = pairgrep { BLOCK } @kvlist;
my $count = pairgrep { BLOCK } @kvlist;
自版本 1.29 起。
類似於 perl 的 grep
關鍵字,但將給定的清單解釋為偶數大小的配對清單。它會多次呼叫 BLOCK
,在純量語境中,將 $a
和 $b
設定為來自 @kvlist
的連續配對值。
傳回一個偶數大小的清單,其中包含 BLOCK
在清單語境中傳回 true 的配對,或在純量語境中傳回 配對數目。(因此,請注意,在純量語境中,它傳回的數字大小是它在清單語境中傳回的項目數目的二分之一)。
@subset = pairgrep { $a =~ m/^[[:upper:]]+$/ } @kvlist
與 grep
將 $_
別名設為清單元素一樣,pairgrep
將 $a
和 $b
別名設為給定清單的元素。程式碼區塊對它的任何修改都對呼叫者可見。
my ( $key, $val ) = pairfirst { BLOCK } @kvlist;
my $found = pairfirst { BLOCK } @kvlist;
自 1.30 版起。
類似於 "first" 函式,但將給定的清單解釋為偶數大小的配對清單。它會多次呼叫 BLOCK
,在純量語境中,將 $a
和 $b
設定為來自 @kvlist
的連續配對值。
傳回清單中第一個 BLOCK
在清單語境中傳回 true 的值配對,或如果找不到此類配對,則傳回一個空清單。在純量語境中,它傳回一個簡單的布林值,而不是找到的鍵或值。
( $key, $value ) = pairfirst { $a =~ m/^[[:upper:]]+$/ } @kvlist
與 grep
將 $_
別名設為清單元素一樣,pairfirst
將 $a
和 $b
別名設為給定清單的元素。程式碼區塊對它的任何修改都對呼叫者可見。
my @list = pairmap { BLOCK } @kvlist;
my $count = pairmap { BLOCK } @kvlist;
自版本 1.29 起。
類似於 perl 的 map
關鍵字,但將給定的清單解釋為成對的偶數大小清單。它在清單內容中多次呼叫 BLOCK
,其中 $a
和 $b
設定為來自 @kvlist
的連續成對值。
傳回清單內容中 BLOCK
傳回的所有值的串接,或在標量內容中傳回的項目數目。
@result = pairmap { "The key $a has value $b" } @kvlist
與 map
將 $_
別名設為清單元素一樣,pairmap
將 $a
和 $b
別名設為給定清單的元素。程式區塊對其所做的任何修改都將對呼叫者可見。
請參閱 "已知錯誤" 以取得 pairmap
的已知錯誤和解決方法。
my @values = shuffle @values;
傳回輸入值以隨機順序排列
@cards = shuffle 0..51 # 0..51 in a random order
此函式受 $RAND
變數影響。
my @items = sample $count, @values
自版本 1.54 起。
從輸入清單中隨機選取指定數目的元素。輸入清單中的任何指定位置都將最多選取一次。
如果清單中的項目少於 $count
,則函式將在所有項目都隨機選取後傳回;函式的行為實際上類似於 "shuffle"。
此函式受 $RAND
變數影響。
my @subset = uniq @values
自 1.45 版起。
篩選值清單以移除後續重複項,依據 DWIM 式字串相等性或 undef
測試判斷。保留唯一元素的順序,並保留任何重複組的第一個值。
my $count = uniq @values
在標量內容中,傳回清單中會傳回的元素數目。
此函式將 undef
值視為與空字串不同,且不會產生任何警告。它會原樣保留在傳回的清單中。後續的 undef
值仍被視為與第一個值相同,且將予以移除。
my @subset = uniqint @values
自 1.55 版起。
篩選值清單以移除後續重複項,依據整數數值相等性測試判斷。保留唯一元素的順序,並保留任何重複組的第一個值。傳回清單中的值將強制轉換為整數。
my $count = uniqint @values
在標量內容中,傳回清單中會傳回的元素數目。
請注意,undef
的處理方式與其他數字運算處理方式類似;它與零相等,但如果啟用此類警告,它還會產生警告(use warnings 'uninitialized';
)。此外,傳回清單中的 undef
會強制轉換為數字零,因此 uniqint
傳回的所有值清單都可作為整數正常運作。
my @subset = uniqnum @values
自版本 1.44 起。
根據數字相等性測試,篩選值清單以移除後續重複項。保留唯一元素的順序,並保留任何重複組的第一個值。
my $count = uniqnum @values
在標量內容中,傳回清單中會傳回的元素數目。
請注意,undef
的處理方式與其他數字運算處理方式類似;它與零相等,但如果啟用此類警告,它還會產生警告(use warnings 'uninitialized';
)。此外,傳回清單中的 undef
會強制轉換為數字零,因此 uniqnum
傳回的所有值清單都可作為數字正常運作。
另請注意,多個 IEEE NaN
值會被視為彼此的重複項,無論其有效負載有何差異,且儘管 0+'NaN' == 0+'NaN'
會產生 false。
my @subset = uniqstr @values
自 1.45 版起。
根據字串相等性測試,篩選值清單以移除後續重複項。保留唯一元素的順序,並保留任何重複組的第一個值。
my $count = uniqstr @values
在標量內容中,傳回清單中會傳回的元素數目。
請注意,undef
的處理方式與其他字串運算處理方式類似;它與空字串相等,但如果啟用此類警告,它還會產生警告(use warnings 'uninitialized';
)。此外,傳回清單中的 undef
會強制轉換為空字串,因此 uniqstr
傳回的所有值清單都可作為字串正常運作。
my @values = head $size, @list;
自版本 1.50 起。
從 @list
傳回前 $size
個元素。如果 $size
為負數,則從 @list
傳回所有元素,但最後 $size
個元素除外。
@result = head 2, qw( foo bar baz );
# foo, bar
@result = head -2, qw( foo bar baz );
# foo
my @values = tail $size, @list;
自版本 1.50 起。
從 @list
傳回最後 $size
個元素。如果 $size
為負數,則從 @list
傳回所有元素,但前 $size
個元素除外。
@result = tail 2, qw( foo bar baz );
# bar, baz
@result = tail -2, qw( foo bar baz );
# baz
my @result = zip [1..3], ['a'..'c'];
# [1, 'a'], [2, 'b'], [3, 'c']
自版本 1.56 起。
傳回陣列參考清單,由給定陣列參考清單中的元素組成。傳回清單中的每個陣列由給定輸入陣列中每個對應位置的元素組成。如果任何輸入陣列在其他陣列之前用完元素,則會將 undef
插入結果中以填補空白。
zip
函數特別方便用於同時使用 foreach
迴圈迭代多個陣列,從每個陣列中取一個元素
foreach ( zip \@xs, \@ys, \@zs ) {
my ($x, $y, $z) = @$_;
...
}
給 List::MoreUtils 使用者的備註:此函數的行為與 List::MoreUtils::zip
不同,但實際上是非原型等效於 List::MoreUtils::zip_unflatten
。此函數不套用原型,所以請務必使用陣列參考呼叫它。
對於類似於 List::MoreUtils
中 zip
函數的函數,請參閱 mesh。
my @result = zip_shortest ...
函數的一個變體,在給定不同長度的輸入陣列時,其行為有所不同。zip_shortest
會在任何一個輸入陣列用完元素時立即停止,捨棄其他陣列中任何剩餘的未使用值。
my @result = zip_longest ...
zip_longest
是 zip
函數的別名,僅提供以明確說明其行為與 zip_shortest
相比。
my @result = mesh [1..3], ['a'..'c'];
# (1, 'a', 2, 'b', 3, 'c')
自版本 1.56 起。
傳回從給定陣列參考清單的元素收集的項目清單。傳回清單中的每個項目區段由給定輸入陣列中每個對應位置的元素組成。如果任何輸入陣列在其他陣列之前用完元素,則會將 undef
插入結果中以填補空白。
這類似於 zip,不同之處在於結果中的所有範圍都以一個長扁平清單傳回,而不是組合成單獨的陣列。
由於它傳回一個扁平的項目清單,因此 mesh
函數特別適用於從兩個單獨的鍵和值陣列建立雜湊
my %hash = mesh \@keys, \@values;
my $href = { mesh \@keys, \@values };
注意給 List::MoreUtils 的使用者:此函式為 List::MoreUtils::mesh
或 List::MoreUtils::zip
(它們本身是彼此的別名)的非原型等效函式。此函式不套用原型,因此請務必使用對陣列的參照來呼叫它。
my @result = mesh_shortest ...
my @result = mesh_longest ...
這些變異與 zip 的變異類似,在於當其中一個輸入清單在其他清單之前用完元素時,它們的行為不同。
local $List::Util::RAND = sub { ... };
自版本 1.54 起。
此套件變數由需要產生亂數的程式碼(例如 "shuffle" 和 "sample" 函式)使用。如果設定為 CODE 參照,它會提供 perl 內建 rand()
函式的替代方案。當需要新的亂數時,此函式會在沒有引數的情況下被呼叫,並預期傳回浮點值,其中只會使用小數部分。
https://rt.cpan.org/Ticket/Display.html?id=95409
如果傳給 "pairmap" 的程式碼區塊包含被傳回閉包擷取的詞彙變數,且閉包在區塊已重新用於下一個反覆運算後執行,這些詞彙變數將看不到正確的值。例如
my @subs = pairmap {
my $var = "$a is $b";
sub { print "$var\n" };
} one => 1, two => 2, three => 3;
$_->() for @subs;
會錯誤地列印
three is 3
three is 3
three is 3
這是因為使用 MULTICALL
對程式碼區塊進行效能最佳化,這表示不會為每個呼叫區塊分配新的 SV。相反地,會為每個反覆運算重新指派相同的 SV,且所有閉包將共用在最後一個反覆運算中看到的數值。
要解決這個錯誤,請使用第二組大括號將程式碼包起來。這會建立一個內部區塊,以擊敗 MULTICALL
邏輯,並在每次都確實分配新的 SV
my @subs = pairmap {
{
my $var = "$a is $b";
sub { print "$var\n"; }
}
} one => 1, two => 2, three => 3;
此錯誤只會影響由區塊產生但之後使用的閉包。在區塊執行期間只使用的詞彙變數會為每個呼叫取得其個別值,如同一般。
由於 uniqnum()
比較數字的方式,它無法區分過於龐大而無法放入原生平台類型中的大數字(特別是大整數)之間的差異。例如,
my $x = Math::BigInt->new( "1" x 100 );
my $y = $x + 1;
say for uniqnum( $x, $y );
只會列印 $x
的值,認為 $y
是數值相等的數值。此錯誤不會影響 uniqstr()
,它會正確地觀察到這兩個值字串化為不同的字串。
以下是已要求新增的功能,但我一直不願意新增,因為它們在 perl 中很容易實作
# How many elements are true
sub true { scalar grep { $_ } @_ }
# How many elements are false
sub false { scalar grep { !$_ } @_ }
版權所有 (c) 1997-2007 Graham Barr <gbarr@pobox.com>。保留所有權利。此程式為免費軟體;您可以在與 Perl 相同的條款下重新散布或修改它。
最近新增的功能和目前的維護由 Paul Evans、<leonerd@leonerd.org.uk> 負責。