tags:

views:

55

answers:

3

I needed to build a variable depth hash data structure in perl. Eventually I found this piece of code:


#!/usr/bin/perl -w
use strict;

my %hash;
my $value = "foo";
my @cats = qw(a b c d);

my $p = \%hash;
foreach my $item (@cats) {
 $p->{$item} = {} unless exists($p->{$item});
 $p = $p->{$item};
}

My question is how and why it works. I thought I knew how perl worked. At no point in this code do I see \%hash value being reset and it appears that $p (which is a local variable) is reset on every loop. I could even see it with data dumper: Running:


#!/usr/bin/perl
use warnings;
use strict;
use Data::Dumper;

my %hash;
my $value = "foo";
my @cats = qw(a b c d);

my $p = \%hash;
foreach my $item (@cats) {
 print "BEFORE:\n";
 print Dumper(\%hash);
 #print Dumper($p);
 $p->{$item} = {} unless exists($p->{$item});
 $p = $p->{$item};
 print "AFTER:\n";
 print Dumper(\%hash);
 #print Dumper($p);
}

And Then uncommenting the line with

#print Dumper($p)
CLEARLY shows $p being a new variable every time.

How does \%hash gets build if $p is reset every time?

+4  A: 

$p isn't being "reset" each time; it's being set to the next lower level in the hash, the one just added. At this point it is just an empty hashref, {}, because it will be filled in on the next loop iteration.

I'm not sure how you are concluding that $p is a new variable each time, but this is incorrect.. it's just pointing to a new location in the %hash structure. Since it was declared outside the loop, it doesn't lose its value as the loop iterates.

Ether
+1  A: 

Add an explicit marker to the hash to which $p refers:

 ...
 print "AFTER:\n";
 $p->{'$p'} = 'is here!';
 print Dumper(\%hash);
 delete $p->{'$p'};
}

Then the last couple of AFTER dumps look like

AFTER:
$VAR1 = {
  'a' => {
    'b' => {
      'c' => {
        '$p' => 'is here!'
      }
    }
  }
};

AFTER:
$VAR1 = {
  'a' => {
    'b' => {
      'c' => {
        'd' => {
          '$p' => 'is here!'
        }
      }
    }
  }
};

Yes, at each new depth, $p refers to an empty hash, but take a step back to see the overall structure of %hash.

Greg Bacon
Thank you very much. That also helped clear things up
fflyer05
+1  A: 

Wanna see something cool? This does the same thing (only you never see it assign the hashref!)

my $p = \\%hash; # $p is a reference to a hashref
foreach my $item (@cats) {
    # because $p references a reference, you have to dereference
    # it to point to the hash.
    $p = \$$p->{$item} unless exists $$p->{$item};
}

The slot is autovivified as soon as you reference it, and when it gets addressed like a hashref, it creates that hashref.

Axeman