0

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

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

4

2 回答 2

2

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

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//[更新:或者通过在标量上下文中使用列表赋值]

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%         --
于 2013-05-27T18:33:17.820 回答
2

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

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

( @feqv_hash{@field_names} = split /\t/, $record, -1 ) == @field_names
    or croak;
于 2013-05-27T19:01:52.623 回答