18

我想通过从数组 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 模块,任何人都可以改进我的实现吗?

4

4 回答 4

25

其他人已经为问题的网格/压缩方面给出了很好的答案,但如果您只是从一组键和一个值创建一个哈希,您可以使用低估的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%    --
于 2013-05-26T03:12:20.423 回答
11

由于您提供了 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 没有元组类型的概念,它几乎就像一个展平操作。

于 2013-05-26T02:28:05.897 回答
5

虽然这个特定的函数已经存在于 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;
于 2013-05-26T02:39:17.847 回答
4

您想使用 List::MoreUtils (或 List::AllUtils )来访问网格又名 zip,请参阅https://metacpan.org/pod/List::MoreUtils#mesh-ARRAY1-ARRAY2-ARRAY3

于 2013-05-26T02:28:44.230 回答