http://www.perlmonks.org?node_id=776546

NateTut has asked for the wisdom of the Perl Monks concerning the following question:

In the code below I would like the Hash filled in the subroutine FillHashOne() to be accessible as a reference in $Hash. However all I get is an empty hash ref.

I think I am missing the section of my brain that is needed to understand references. I have read all the tutorials and have used references many times, but then I run into a situation like this that stymies me.

All help and suggestions are appreciated!

Update: Duh! I messed up my closure and had it inside the subroutine. It works when I fixed that.

#!/usr/bin/perl use strict; use warnings; use constant false => 0; use constant true => 1; $| = true; use Data::Dumper; my $Hash = {}; sub FillHashOne { my %HashOne; { my ($Key, $Value) = @_; if(!defined($Key)) { return(\%HashOne); } else { $HashOne{$Key} = $Value; } } } $Data::Dumper::Indent = 3; $Data::Dumper::Purity = true; $Data::Dumper::Varname = 'Hash'; $Data::Dumper::Quotekeys = true; $Hash->{'One'} = 1; $Hash->{'Two'} = 2; $Hash->{'Three'} = 3; $Hash->{'String'} = 'Foo Bar'; print __LINE__ . ":Main:\$Hash:\n" . Dumper($Hash); for (my $i = 0; $i < 10; $i++) { FillHashOne($i, "Value:\[$i\]"); } $Hash->{'HashOne'} = FillHashOne(undef()); print __LINE__ . ":Main:\$Hash:\n" . Dumper($Hash);


Updated Code:

#!/usr/bin/perl use strict; use warnings; use constant false => 0; use constant true => 1; $| = true; use Data::Dumper; my $Hash = {}; { my %HashOne; sub FillHashOne { my ($Key, $Value) = @_; if(!defined($Key)) { return(\%HashOne); } else { $HashOne{$Key} = $Value; } } } $Data::Dumper::Indent = 3; $Data::Dumper::Purity = true; $Data::Dumper::Varname = 'Hash'; $Data::Dumper::Quotekeys = true; $Hash->{'One'} = 1; $Hash->{'Two'} = 2; $Hash->{'Three'} = 3; $Hash->{'String'} = 'Foo Bar'; print __LINE__ . ":Main:\$Hash:\n" . Dumper($Hash); for (my $i = 0; $i < 10; $i++) { FillHashOne($i, "Value:\[$i\]"); } $Hash->{'HashOne'} = FillHashOne(undef()); print __LINE__ . ":Main:\$Hash:\n" . Dumper($Hash);

Replies are listed 'Best First'.
Re: Not So Complex Hash Question
by davorg (Chancellor) on Jul 01, 2009 at 19:47 UTC

    Your code has a number of interesting quirks, but the main problem is caused by the fact that %HashOne is defined as a lexical variable within the FillHashOne() subroutine. This means that a new, empty, version of the variable is created each time you enter the subroutine and destroyed each time you leave the subroutine.

    This is why state variables were introduced in Perl 5.10. Your code works as you want it to if you rewrite it like this:

    #!/usr/bin/perl use feature ':5.10'; use strict; use warnings; use constant false => 0; use constant true => 1; $| = true; use Data::Dumper; my $Hash = {}; sub FillHashOne { state %HashOne; { my ($Key, $Value) = @_; if(!defined($Key)) { return(\%HashOne); } else { $HashOne{$Key} = $Value; } } } $Data::Dumper::Indent = 3; $Data::Dumper::Purity = true; $Data::Dumper::Varname = 'Hash'; $Data::Dumper::Quotekeys = true; $Hash->{'One'} = 1; $Hash->{'Two'} = 2; $Hash->{'Three'} = 3; $Hash->{'String'} = 'Foo Bar'; print __LINE__ . ":Main:\$Hash:\n" . Dumper($Hash); for (my $i = 0; $i < 10; $i++) { FillHashOne($i, "Value:\[$i\]"); } $Hash->{'HashOne'} = FillHashOne(undef()); print __LINE__ . ":Main:\$Hash:\n" . Dumper($Hash);

    For it to work in earlier versions of Perl, you need to move the definition of %HashOne out of the subroutine. It's probably a good idea to keep both the variable definition and the subroutine in the same naked block. Keeping your idiosyncratic indentation style, it would look like this:

    #!/usr/bin/perl use strict; use warnings; use constant false => 0; use constant true => 1; $| = true; use Data::Dumper; my $Hash = {}; { my %HashOne; sub FillHashOne { my ($Key, $Value) = @_; if(!defined($Key)) { return(\%HashOne); } else { $HashOne{$Key} = $Value; } } } $Data::Dumper::Indent = 3; $Data::Dumper::Purity = true; $Data::Dumper::Varname = 'Hash'; $Data::Dumper::Quotekeys = true; $Hash->{'One'} = 1; $Hash->{'Two'} = 2; $Hash->{'Three'} = 3; $Hash->{'String'} = 'Foo Bar'; print __LINE__ . ":Main:\$Hash:\n" . Dumper($Hash); for (my $i = 0; $i < 10; $i++) { FillHashOne($i, "Value:\[$i\]"); } $Hash->{'HashOne'} = FillHashOne(undef()); print __LINE__ . ":Main:\$Hash:\n" . Dumper($Hash);
    --

    See the Copyright notice on my home node.

    Perl training courses