in reply to Re^2: re-useing a hash
in thread re-useing a hash
Lets examine a quick example:
Ok, it looks a bit convoluted, definitely contrived, but hey it's just an example. Running through the program through our brains, we're creating a hash with a key of 0 to 10, and setting its value to 3*x, then calling bar() using that index. Bar increments that hash element x by one, then calls foo() which adds 3 more so the value of %hash{0} at this point should be 4. Then we call foo() again, so we're up to 7. %hash{1} should similarly get a value of 3+1+3+3=10 etc.#!/usr/bin/perl use strict; use warnings; use vars qw(%hash); sub foo { $hash{$_[0]}+=3; } sub bar { $hash{$_[0]}++; foo($_[0]); } foreach my $i (0..10) { my %hash; $hash{$i} = $i*3; bar($i); foo($i); foreach my $j (sort keys %hash) { print "hash{$j} = $hash{$j}\n"; } } print "done\n"; foreach my $j (sort keys %hash) { print "hash{$j} = $hash{$j}\n"; }
Oh, and each time through we should end up with a new empty hash too, so if we see repeaters, then the hash is sticking around
Run the code and we get this output:
Well that wasn't what we expected was it. We don't have any repeaters, so we are getting a new hash, but the values aren't correct. And why do we have a %hash with values in it to print at the end?hash{0} = 0 hash{1} = 3 hash{2} = 6 hash{3} = 9 hash{4} = 12 hash{5} = 15 hash{6} = 18 hash{7} = 21 hash{8} = 24 hash{9} = 27 hash{10} = 30 done hash{0} = 7 hash{1} = 7 hash{10} = 7 hash{2} = 7 hash{3} = 7 hash{4} = 7 hash{5} = 7 hash{6} = 7 hash{7} = 7 hash{8} = 7 hash{9} = 7
Scoping is causing issues for you. Neither foo() nor bar() know anything about the private %hash we created by the "my %hash;" line within the foreach loop. bar() is creating a new global version of that hash and hash entry when you call it, and foo() is likewise acting on that new global version of %hash. That is why the initially printed values are only multiplied by three and the remaining global %hash all have values of 7 once you're out of the foreach loop .
So, how do you fix the problem? Either don't use "my %hash;", and continue to use a global %hash variable (not really recommended, but it works) or start passing a reference to the hash to the subroutines that need to access it. If you don't use "my %hash", then the "undef %hash;" line will be needed at the end of your foreach loop.
Update: You can't use a "my %hash" in this case, as you can't take a reference to a my variable (at least I'm pretty sure you can't) since it doesn't exist on the glob table like a normal variable does. Just add the "undef %hash;" at the end of the foreach loop and it should start working more correctly than it currently does.
-Scott
Update: Fixed various typo's... Update 2: Made the explaination a bit more clear, and fixed the "reference" idea