Perl - 将两个数组“拉链”在一起的内置函数?
Posted
技术标签:
【中文标题】Perl - 将两个数组“拉链”在一起的内置函数?【英文标题】:Perl - built-in function to "zipper" together two arrays? 【发布时间】:2013-05-21 06:36:22 【问题描述】:我想通过从数组 A 中获取第一个元素,从数组 B 中获取第一个元素,将两个长度相等的数组合并为一个数组; A 中的第二个元素,B 中的第二个元素等。以下程序说明了该算法:
# file zipper.pl
use strict;
use warnings;
use 5.010;
my @keys = qw/abel baker charlie dog easy fox/;
my @values = qw/a b c d e f/;
# ==> Is there a builtin function that is equivalent of zipper()? <==
#
my %hash = zipper( \@keys, \@values );
while ( my ( $k, $v ) = each %hash )
say "$k=$v";
# zipper(): Take two equal-length arrays and merge them (one from A, one from B,
# another from A, another from B, etc.) into a single array.
#
sub zipper
my $k_ref = shift;
my $v_ref = shift;
die "Arrays must be equal length" if @$k_ref != @$v_ref;
my $i = 0;
return map $k_ref->[ $i++ ], $_ @$v_ref;
输出
$ ./zipper.pl
easy=e
dog=d
fox=f
charlie=c
baker=b
abel=a
我想知道我是否忽略了 Perl 中的一个与 zipper() 等效的内置函数。它将位于程序的最内层循环,并且需要尽可能快地运行。如果没有内置或 CPAN 模块,任何人都可以改进我的实现吗?
【问题讨论】:
【参考方案1】:其他人已经为问题的网格/zip 方面给出了很好的答案,但如果您只是从一组键和一个值创建一个哈希值,您可以使用未受重视的 hash slice 来完成它。
#!/usr/bin/env perl
use strict;
use warnings;
my @keys = qw/abel baker charlie dog easy fox/;
my @values = qw/a b c d e f/;
my %hash;
@hash@keys = @values;
use Data::Dumper;
print Dumper \%hash;
附录
我开始思考为什么人们会选择一种方法而不是另一种。我个人认为 slice 实现和 zip 一样具有可读性,但其他人可能不同意。如果您经常这样做,您可能会关心速度,在这种情况下,切片形式会更快。
#!/usr/bin/env perl
use strict;
use warnings;
use List::MoreUtils qw/zip/;
use Benchmark qw/cmpthese/;
my @keys = qw/abel baker charlie dog easy fox/;
my @values = qw/a b c d e f/;
cmpthese( 100000,
zip => sub
my %hash = zip @keys, @values;
,
slice => sub
my %hash;
@hash@keys = @values;
,
);
结果:
Rate zip slice
zip 51282/s -- -34%
slice 78125/s 52% --
【讨论】:
确实很好。和往常一样,我没有提出真正的问题,而是假设了一个解决方案,然后以这种方式表达了这个问题。您的解决方案无疑是最好的方法。进一步的启示:我实际上是在尝试初始化一个 Tie::IxHash - 我一直在使用 OO 形式,我不相信它支持哈希切片,但出于我的目的,我认为简单的非 OO 接口会没事。 我还应该说,我知道很多人不喜欢 Perl 中符号的变化,但我认为在这种情况下,它确实有助于直观地检查正在发生的事情。也许只有我 :-)【参考方案2】:由于您提供了 CPAN 想法,因此有 List::MoreUtils
和 zip
。
use List::MoreUtils qw(zip);
my @keys = qw/abel baker charlie dog easy fox/;
my @values = qw/a b c d e f/;
my @zipped = zip @keys, @values;
@zipped
的内容是:
abel, a, baker, b, charlie, c, dog, d, easy, e, fox, f
使用此方法的好处在于,如果您愿意,您可以压缩两个以上的列表。由于 Perl 没有元组类型的概念,它几乎就像一个展平操作。
【讨论】:
我将键和值组合到一个数组中,当分配给哈希时,会将哈希初始化为这些键=值对。实际上,我只是将 zip 返回的列表直接分配给哈希。 你可以这样做。my %hash = zip @keys, @values;
。但是如果你正在做一个哈希,为什么不只使用一个切片呢? my %hash; @hash@keys = @values;
nm,刚刚看到@Joel 的回答。
@friedo 这个问题让我有点困惑。我不确定发布者是否想要一个数组或哈希作为结果。他的代码说的是一回事,说的是另一回事。
我以为我需要一个数组来初始化哈希,所以在我试图简化问题时,我实际上把它弄糊涂了!很抱歉造成混乱。【参考方案3】:
虽然这个特定函数已经存在于 List::MoreUtils 中,但您可以使用原型为自己的数组函数赋予内置数组运算符的外观(如 push
、shift
、pop
):
sub zipper (++) # perldoc perlsub
my ($k, $v) = @_;
die "Arrays must be equal length" if @$k != @$v;
my $i;
return map $k->[$i++], $_ @$v
%hash = zipper @keys, @values;
%hash = zipper \@keys, \@values;
%hash = zipper $key_aref, $value_aref;
【讨论】:
关于原型的说明:我同意+
比\@
更可取,但前者仅从v14 开始可用。 +
原型还接受任何标量,或者在呈现@array
或%hash
变量时进行引用。在这里不使用它可能更干净。
@amon,因为原型与 Perl 中的严格性或类型检查无关,所以我不介意散列的可能性。但是版本要求很有趣——我只在单行中使用 systemperl,所以我忘了考虑什么时候添加这个功能。【参考方案4】:
您想使用 List::MoreUtils (或 List::AllUtils )来访问网格(也称为 zip),请参阅 https://metacpan.org/pod/List::MoreUtils#mesh-ARRAY1-ARRAY2-ARRAY3
【讨论】:
"mesh" - 啊,谢谢你给了我想要的词。以上是关于Perl - 将两个数组“拉链”在一起的内置函数?的主要内容,如果未能解决你的问题,请参考以下文章