chidori
chidori

Reputation: 1112

what is the difference between taking reference to a variable using \ and {},[] in perl?

below code works fine but if I replace push @array,{%hash} with push @array,\%hash then it doesn't. Can someone please help me understand the difference. I believe {%hash} refers to an anonymous hash. Does it mean a anonymous hash lives longer than a reference to a named 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;

output

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

UPDATE Below is the actual code I am working on. I think in this case taking copy of the reference is the only possible solution I believe. Please correct me if I am wrong.

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

Upvotes: 3

Views: 278

Answers (2)

ikegami
ikegami

Reputation: 386331

You can't really compare \ to {} and [] since they don't do the same thing at all.

{ LIST } is short for my %anon = LIST; \%anon

[ LIST ] is short for my @anon = LIST; \@anon


Maybe you meant to compare

  1.  

    my %hash = ...;
    push @a, \%hash;
    
  2.  

    push @a, { ... };
    
  3.  

    my %hash = ...;
    push @a, { %hash };
    

The first snippet places a reference to %hash in @a. This is presumably found in a loop. As long as my %hash is found in the loop, a reference to a new hash will be placed in @a each time.

The second snippet does the same, just using an anonymous hash.

The third snippet makes a copy of %hash, and places a reference to that copy in @a. It gives the impression of wastefulness, so it's discouraged. (It's not actually not that wasteful because it allows %hash to be reused.)


You could also write your code

# 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;
}

or

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;
    }
}

Better yet, let's use a proper CSV parser.

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;
}

Upvotes: 3

simbabque
simbabque

Reputation: 54373

Both \%hash and {%hash} create references, but they reference two different things.

\%hash is a ref to %hash. If dereferenced, its values will change with the values in %hash.

{%hash} creates a new anonymous hash reference from the values in %hash. It creates a copy. It's the simplest way of creating a shallow copy of a data structure in Perl. If you alter %hash, this copy is not affected.


How long a variable lives has nothing to do with what kind the variable is, or how it was created. Only the scope is relevant for that. References in Perl are a special case here, because there is an internal ref counter that keeps track of references to a value, so that it is kept alive if there are still references around somewhere even if it goes out of scope. That's why this works:

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

If you want to disassociate the reference from the initial value, you need to turn it into a weak reference via weaken from Scalar::Util. That way, the ref count will not be influenced by it, but it will still be related to the value, while a copy would not be.

See perlref and perlreftut for more information on references. This question deals with how to see the ref count. A description for that is also available in the chapter Reference Counts and Mortality in perlguts.

Upvotes: 13

Related Questions