在perl中使用\和{},[]引用变量有什么区别?

时间:2017-05-09 15:43:29

标签: perl

下面的代码工作正常,但如果我用push @array,{%hash}替换push @array,\%hash,那么它就没有了。有人可以帮我理解差异。我相信{%hash}是指匿名哈希。这是否意味着匿名哈希的存在时间长于对命名哈希(\%hash)的引用。

use strict;
use warnings;
use Data::Dumper;
my @array;
my %hash;
%hash = ('a' => 1,
         'b' => 2,
         'c' => 3,);

push @array,{%hash};

%hash = ('e' => 1,
         'f' => 2,
         'd' => 3,);

push @array,{%hash};

print Dumper \@array;

输出

$VAR1 = [
          {
            'c' => 3,
            'a' => 1,
            'b' => 2
          },
          {
            'e' => 1,
            'd' => 3,
            'f' => 2
          }
        ];

更新 以下是我正在处理的实际代码。我认为在这种情况下,参考的副本是我认为唯一可行的解​​决方案。如果我错了,请纠正我。

use Data::Dumper;
use strict;
use warnings;

my %csv_data;
my %temp_hash;
my @cols_of_interest = qw(dev_file test_file diff_file status);
<DATA>; #Skipping the header
while (my $row = <DATA>) {
    chomp $row;
    my @array = split /,/,$row;
    @temp_hash{@cols_of_interest} = @array[3..$#array]; 
    push @{$csv_data{$array[0]}{$array[1] . ':' . $array[2]}},{%temp_hash};
}
print Dumper \%csv_data;

__DATA__
dom,type,id,dev_file,test_file,diff_file,status
A,alpha,1234,dev_file_1234_1.txt,test_file_1234_1.txt,diff_file_1234_1.txt,pass
A,alpha,1234,dev_file_1234_2.txt,test_file_1234_2.txt,diff_file_1234_2.txt,fail
A,alpha,1234,dev_file_1234_3.txt,test_file_1234_3.txt,diff_file_1234_3.txt,pass
B,beta,4567,dev_file_4567_1.txt,test_file_4567_1.txt,diff_file_4567_1.txt,pass
B,beta,4567,dev_file_4567_2.txt,test_file_4567_2.txt,diff_file_4567_2.txt,fail
C,gamma,3435,dev_file_3435_1.txt,test_file_3435_1.txt,diff_file_3435_1.txt,pass
D,hexa,6768,dev_file_6768_1.txt,test_file_6768_1.txt,diff_file_6768_1.txt,fail

2 个答案:

答案 0 :(得分:13)

\%hash{%hash}都会创建引用,但它们引用了两个不同的东西。

\%hash是对%hash的引用。如果取消引用,其值将随%hash中的值而变化。

{%hash}根据%hash中的值创建新的匿名哈希引用。它会创建副本。这是在Perl中创建shallow copy数据结构的最简单方法。如果您更改%hash,则此副本不会受到影响。

变量的存在时间与变量的类型或创建方式无关。只有scope与此相关。 Perl中的引用在这里是一个特殊情况,因为有一个内部引用计数器可以跟踪对值的引用,因此如果某个地方仍有引用,即使它超出范围,它也会保持活动状态。这就是为什么这样做的原因:

sub frobnicate {
    my %hash = ( foo => 'bar' );
    return \%hash;
}

如果您想要将参考与初始值取消关联,则需要通过weak reference通过weaken将其转换为Scalar::Util。这样,引用计数不会受其影响,但它仍然与值相关,而副本则不会。

有关参考资料的详情,请参阅perlrefperlreftutThis question处理如何查看引用计数。有关这方面的说明,请参见Reference Counts and Mortality in perlguts一章。

答案 1 :(得分:3)

您无法真正将\{}[]进行比较,因为它们根本不会做同样的事情。

{ LIST }my %anon = LIST; \%anon

的缩写

[ LIST ]my @anon = LIST; \@anon

的缩写

也许你打算比较

  1. my %hash = ...;
    push @a, \%hash;
    
  2. push @a, { ... };
    
  3. my %hash = ...;
    push @a, { %hash };
    
  4. 第一个代码段在%hash中引用@a的引用。这可能是在一个循环中找到的。只要在循环中找到my %hash,每次都会在@a中放置对新哈希的引用。

    第二个片段也是如此,只使用匿名哈希。

    第三个代码段会复制%hash,并在@a中放置对该副本的引用。它给人的印象是浪费,所以不要气馁。 (实际上并不浪费,因为它允许重用%hash。)

    您也可以编写代码

    # In reality, the two blocks below are probably the body of one sub or one loop.
    
    {
       my %hash = (
          a => 1,
          b => 2,
          c => 3,
       );
    
       push @a, \%hash;
    }
    
    {
       my %hash = (
          d => 3,
          e => 1,
          f => 2,
       );
    
       push @a, \%hash;
    }
    

    push @a, {
       a => 1,
       b => 2,
       c => 3,
    };
    
    push @a, {
       d => 3,
       e => 1,
       f => 2,
    };
    
    my @cols_of_interest = qw( dev_file test_file diff_file status );
    
    my %csv_data;
    if (defined( my $row = <DATA> )) {
        chomp $row;
        my @cols = split(/,/, $row);
    
        my %cols_of_interest = map { $_ => 1 } @cols_of_interest;
        my @cols_to_delete = grep { !$cols_of_interest{$_} } @cols;
    
        while ( my $row = <DATA> ) {
            chomp $row;
            my %row; @row{@cols} = split(/,/, $row);
            delete @row{@cols_to_delete};
            push @{ $csv_data{ $row{dev_file} }{ "$row{test_file}:$row{diff_file}" } }, \%row;
        }
    }
    

    更好的是,让我们使用正确的CSV解析器。

    use Text::CSV_XS qw( );
    
    my @cols_of_interest = qw( dev_file test_file diff_file status );
    
    my $csv = Text::CSV_XS->new({
        auto_diag => 2,
        binary    => 1,
    });
    
    my @cols = $csv->header(\*DATA);
    
    my %cols_of_interest = map { $_ => 1 } @cols_of_interest;
    my @cols_to_delete = grep { !$cols_of_interest{$_} } @cols;
    
    my %csv_data;
    while ( my $row = $csv->getline_hr(\*DATA) ) {
        delete @$row{@cols_to_delete};
        push @{ $csv_data{ $row->{dev_file} }{ "$row->{test_file}:$row->{diff_file}" } }, $row;
    }