【问题标题】:Perl - assign output of split() to hash slice - detecting length mismatchPerl - 将 split() 的输出分配给哈希切片 - 检测长度不匹配
【发布时间】:2013-05-22 15:07:38
【问题描述】:

我正在将制表符分隔值的记录转换为哈希,如下所示:

my @field_names = qw(foo bar xyzzy);
my $record = "33\t45\t78\n";
my %feqv_hash;
@feqv_hash{@field_names} = split /\t/, $record;

创建%feqv_hash

{ foo => 33, bar => 45, xyzzy => 78 }

我希望能够尽快确保 $record 具有与 @field_names 相同数量的值。

这是我能想到的最好的:

my @field_names = qw(foo bar xyzzy);
my $record = "33\t45\t78\n";
my @field_values = split /\t/, $record;
croak if @field_names != @field_values;
my %feqv_hash;
@feqv_hash{@field_names} = @field_values;

有没有一种方法可以更快地执行? (例如,不需要临时数组 @field_values

【问题讨论】:

  • $record中制表符分隔字段的个数可以用my $count = 1 + $record =~ tr/\t//;获取

标签: perl hash split slice


【解决方案1】:

这肯定是过早的优化;编写对可能的受众来说最易读的代码,而不是为了一些实际上无法衡量的性能提升。

也就是说,在标量上下文中,切片赋值本身(与所有列表赋值一样)将返回右侧元素的计数,因此您需要做的就是:

( @feqv_hash{@field_names} = split /\t/, $record, -1 ) == @field_names
    or croak;

【讨论】:

    【解决方案2】:

    首先,您想使用-1 而不是0 作为split 的第三个参数,这样您就不会删除任何存在但为空的字段。

    my @field_names = qw(foo bar xyzzy);
    my $record = "33\t45\t78\n";
    my %feqv_hash;
    @feqv_hash{@field_names} = split /\t/, $record, -1;
    

    让我们看看检查有多慢。

    use strict;
    use warnings;
    use Benchmark qw( timethese );
    use Carp      qw( croak );
    
    my %tests = (
       without => <<'__EOI__',
          my %feqv_hash;
          @feqv_hash{@field_names} = split /\t/, $record, -1;
    __EOI__
       with => <<'__EOI__',
          my @field_values = split /\t/, $record, -1;
          croak if @field_names != @field_values;
          my %feqv_hash;
          @feqv_hash{@field_names} = @field_values;
    __EOI__
    );    
    
    $_ = 'use strict; use warnings; our @field_names; our $record; '.$_
       for values %tests;
    
    {
       local our @field_names = qw(foo bar xyzzy);
       local our $record = "33\t45\t78\n";
       timethese(-3, \%tests);
    }
    

    结果:

    without check: 2.7 microseconds
    with check:    4.1 microseconds
                   ----------------
    check:         1.4 microseconds
    

    检查需要 1.4 微秒。我不知道你为什么认为有问题。


    但是可以通过使用tr/\t// 扫描字符串来将时间缩短一半。 [Upd:或者在标量上下文中使用列表赋值]

    use strict;
    use warnings;
    use Benchmark qw( cmpthese );
    use Carp      qw( croak );
    
    my %tests = (
       temp_array => <<'__EOI__',
          my @field_values = split /\t/, $record, -1;
          croak if @field_names != @field_values;
          my %feqv_hash;
          @feqv_hash{@field_names} = @field_values;
    __EOI__
       tr => <<'__EOI__',
          croak if @field_names != 1 + $record =~ tr/\t//;
          my %feqv_hash;
          @feqv_hash{@field_names} = split /\t/, $record, -1;
    __EOI__
       aassign => <<'__EOI__',
          my %feqv_hash;
          ( @feqv_hash{@field_names} = split /\t/, $record, -1 ) == @field_names
             or croak;
    __EOI__
    );    
    
    $_ = 'use strict; use warnings; our @field_names; our $record; '.$_
       for values %tests;
    
    {
       local our @field_names = qw(foo bar xyzzy);
       local our $record = "33\t45\t78\n";
       cmpthese(-3, \%tests);
    }
    

    结果:

                   Rate temp_array         tr    aassign
    temp_array 233472/s         --       -30%       -36%
    tr         334671/s        43%         --        -8%
    aassign    362326/s        55%         8%         --
    

    【讨论】:

    • 我将@ysth 的解决方案添加到您的框架中。它的表现优于 tr,但没有我预期的那么多(4%)。 (框架+1)
    猜你喜欢
    • 2011-07-20
    • 1970-01-01
    • 2013-08-10
    • 1970-01-01
    • 1970-01-01
    • 2012-01-09
    • 1970-01-01
    • 2021-04-23
    • 1970-01-01
    相关资源
    最近更新 更多