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
来减少 @list
,每次设置 $a
和 $b
。第一次调用将使用 $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
被设置为给定列表的第一个元素,然后对列表中剩余的每个元素(设置为 $b
)调用 BLOCK
一次,结果被捕获以返回,并成为 $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
的每个元素。如果任何元素使 BLOCK
返回真值,则 any
返回真值。如果 BLOCK
从未返回真值或 @list
为空,则返回假值。
许多在条件语句中使用 grep
的情况可以使用 any
代替,因为它可以在第一个真值结果后短路。
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
返回真值。如果任何元素返回假值,则返回假值。如果 BLOCK
从未返回假值或 @list
为空,则返回真值。
注意:由于 XS 问题,传递的块可能能够直接访问外部 @_
。这不是故意的,在调试器下会中断。
my $bool = none { BLOCK } @list;
my $bool = notall { BLOCK } @list;
自版本 1.33 起。
类似于 "any" 和 "all",但返回值意义相反。none
仅在 @list
中没有值导致 BLOCK
返回真值时返回真值,而 notall
仅在并非所有值都返回真值时返回真值。
注意:由于 XS 问题,传递的块可能能够直接访问外部 @_
。这不是故意的,在调试器下会中断。
my $val = first { BLOCK } @list;
类似于 grep
,它评估 BLOCK
,将 $_
依次设置为 @list
的每个元素。first
返回 BLOCK
结果为真值的第一个元素。如果 BLOCK
从未返回真值或 @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 的启发,使用一个偶数大小的键值对列表。这些键值对可以是哈希中的键值关联,也可以只是值列表。所有函数都将保留键值对的原始顺序,并且不会因多个键值对具有相同的“键”值而混淆 - 甚至不需要第一个键值对是普通字符串。
注意:在撰写本文时,以下接受块的 pair*
函数不会修改块内的 $_
值,而是使用 $a
和 $b
全局变量进行操作。事实证明,这是一种糟糕的设计,因为它排除了提供 pairsort
函数的能力。更好的做法是将类似键值对的对象作为 2 元素数组引用传递给 $_
,类似于 pairs
函数的返回值。在未来的某个版本中,可能会添加此行为。
在此之前,请提醒用户不要依赖 $_
的值在控制块内外保持不变。特别是,以下示例不安全
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
但是请注意,这些版本在标量上下文中不会表现得那么好。
最后,此技术可用于实现对键值对列表的排序;例如
@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()
比较数字的方式,它无法区分太大而无法放入本地平台类型的 bignum(尤其是 bigint)之间的差异。例如,
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 <[email protected]>。保留所有权利。本程序是自由软件;您可以在与 Perl 本身相同的条款下重新分发和/或修改它。
Paul Evans <[email protected]> 进行了最近的添加和当前维护。