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::MoreUtilszip

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 中,但您可以使用原型为自己的数组函数赋予内置数组运算符的外观(如 pushshiftpop):

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 - 将两个数组“拉链”在一起的内置函数?的主要内容,如果未能解决你的问题,请参考以下文章

内置函数的补充

拉链法解决hashtable冲突问题

如何将两个数组分配给 Perl 中的哈希?

Perl,将数字键哈希转换为数组

python中重要的内置函数

将两个或多个数组传递给 Perl 子例程