I wanted to have a hash of arrays of counters, with initial values of 0. Here is my first attempt at the code:
my @names = ("", a, b, c);
my %hsh = ();
for $i (1..3)
{
# I expected this line to give me a fresh new array each time - it did not.
$hsh{$names[$i]} = (0, 0, 0); # Assignment line #
doprint($i); #see below
}
#output is: ARRAY(0x1501128) ARRAY(0x1501128) ARRAY(0x1501128)
We see they are all the same array, which is not desired. To fix the problem, I changed the # Assignment line to this:
$hsh{$names[$i]} = ( );
push(@{$hash{$names[$i]}}, 0, 0, 0);
# output is: ARRAY(0x1510c70) ARRAY(0x1510d18) ARRAY(0x1510dc0)
Now they are all different arrays, and life is good.
If the Assignment line uses (0, 0, $i) I get different arrays (at least here - they might be reused elsewhere)
but I couldn't fool it by using (0, 0, $i - $i) or a more complex formula to generate a 0 value.
Is this some sort of optimization by Perl? Why isn't (0, 0, 0) a new array each time? Would (0, 0) or (0, 0, 0, 0) be different?
For the sake of completeness, here is the print subroutine:
sub doprint
{
my $i = shift;
print (\@{$hsh{$names[$i]}}, " ");
}
---- The following edit is on the morning of Feb 9 after the "there is no array comment" - I wanted to add some code and comments don't allow that very well.
OK: $h{$i} = (0, 0, 0); initializes it to a scalar value of 0.
But, as indicated in the output below, there's an array somewhere.
Did writing $h{$i}[$i] create @-something alongside the scalars?
my %h = ();
sub dotest
{
my $desc = shift;
print " # $desc\n";
for (0, 1, 2)
{
$h{$_}[$_] += 1 + $_;
}
for (0, 1, 2)
{
print " # $_: $h{$_} ";
print "# @{$h{$_}}" . " # $h{$_}[$_] \n";
}
print "\n";
}
%h = ( );
$h{0} = [0, 0, 0];
$h{1} = [0, 0, 0];
$h{2} = [0, 0, 0];
dotest("Initialize to [0, 0, 0]");
%h = ( );
$h{0} = (0, 0, 0);
$h{1} = (0, 0, 0);
$h{2} = (0, 0, 0);
dotest("Initialize to (0,0,0)");
%h = ( );
dotest("No initialization");
%h = ( );
$h{0} = \@{(0, 0, 0)};
$h{1} = \@{(0, 0, 0)};
$h{2} = \@{(0, 0, 0)};
dotest("Initialize to \\\@{(0, 0, 0)}");
# Initialize to [0, 0, 0] - Separate arrays, initialized to zeros.
# 0: ARRAY(0x10ca5e0) # 1 0 0 # 1
# 1: ARRAY(0x10ca778) # 0 2 0 # 2
# 2: ARRAY(0x2709da0) # 0 0 3 # 3
# Initialize to (0,0,0) - scalars AND a shared array?
# 0: 0 # 1 2 3 # 1
# 1: 0 # 1 2 3 # 2
# 2: 0 # 1 2 3 # 3
# No initialization - separate arrays, initialized to null
# 0: ARRAY(0x2709e48) # 1 # 1
# 1: ARRAY(0x10ca718) # 2 # 2
# 2: ARRAY(0x2709da0) # 3 # 3
# Initialize to \@{(0, 0, 0)} - reusing the shared array from previous?
# 0: ARRAY(0x2709d88) # 2 4 6 # 2
# 1: ARRAY(0x2709d88) # 2 4 6 # 4
# 2: ARRAY(0x2709d88) # 2 4 6 # 6
This line doesn't do what you think:
(0, 0, 0)is not an array, it's a list. And you can't store an array (or, indeed, a list) in a hash element. You can only store scalar values in a hash.You can get round this by storing a reference to an array in your hash. And the easiest way to do that is to use the anonymous array constructor (
[ ... ]).