#!/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";
}
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.
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:
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
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?
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 |