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

O light-bearers of perl, help this sorry pilgrim!

I don't undersatnd this idiom

my @keys = qw( 0 0.1 0.3 1 3 10 30 1000 ); my %n; for ( my $mag = 5.0 ; $mag < 9.0 ; $mag += 0.1 ) { @{ $n{ $mag } }{ @keys } = ( 0 ) x @keys; }
which is used here for code that runs (I checked), but I don't understand the @{ $n{ $mag } } part.

It seems kind of like a hash slice, for example

my @keys = qw(a b c d); my %hash; @hash{@keys} = (1) x @keys;
as described at this explanation of the x operator.

But there seems to be more dereferencing, or something, going on... I don't get it. Is this a hash slice or some other thing?

UPDATE: On reflection, I believe my confusion was all about the brackets for setting hash values. Where do you have to leave them out, where can you leave them in... and where does it work both ways? See my post further down.

Replies are listed 'Best First'.
Re: Is this a hash slice?
by Roy Johnson (Monsignor) on Mar 31, 2005 at 15:49 UTC
    Yes, it's a hash slice. But instead of a hash variable name, they're providing a hash reference inside {}s. Wherever you can use a variable name, you can use a block that evaluates to a reference to the same type.

    Caution: Contents may have been coded under pressure.
Re: Is this a hash slice?
by tlm (Prior) on Mar 31, 2005 at 15:55 UTC

    Try this:

    my @keys = qw(a b c d); my $hash_ref; @{ $hash_ref }{@keys} = (1) x @keys; print "$_\n" for keys %$hash_ref; __END__
    In general, $h{x}{y} is shorthand for $h{x}->{y}, meaning that the content of $h{x} is a hash ref, just like $hash_ref above; once dereferenced, it can be used like any other hash.

    the lowliest monk

Re: Is this a hash slice?
by webfiend (Vicar) on Mar 31, 2005 at 18:55 UTC

    Sounds like you've already got the idea, but this is an issue that trips a lot of us up at one time or another. I might as well take a stab at it myself.

    It's hard to look at the confusing bit in isolation. I know, because I just spent 15 minutes trying to describe it without any additional context. Maybe if I did the backwards reading trick I could describe what's going on.

    Here's the original baffling line.

    @{ $n{ $mag } }{ @keys } = ( 0 ) x @keys;

    Here's the line broken down by reading backwards.

    ( 0 ) x @keys # A list of zeroes the same length as @keys = # is assigned to { @keys } # a slice using @keys for the keys @{ # in the hash referred to $n{$mag} # by the value pointed to by $mag in %n. }

    That makes a little more sense, I guess. I don't care for this idiom, personally, but I like my code to be explicit whenever possible - even if it means writing out my intent in more than one statement :-) Oddly enough, though, the most explicit version I could come up with was actually shorter!

    $n{$mag} = { map { $_ => 0 } @keys };

    Well, I suppose I could have used a foreach loop instead, and that would have been nice and explicit also. I must finally be starting to get the hang of map or something.

Re: Is this a hash slice?
by tphyahoo (Vicar) on Mar 31, 2005 at 17:59 UTC
    Thanks. I think I'm beginning to get it. It was easier for me to understand what's going on after I used Data::Dumper to dump the hash, which is actually a HoH (hash of hashes).
    use warnings; use strict; use Data::Dumper; my @keys = qw( 0 0.1); my %n; for ( my $mag = 5.0 ; $mag < 6.0 ; $mag += 0.1 ) { @{ $n{ $mag } }{ @keys } = ( 0 ) x @keys; } print Dumper(\%n); __END__ Outputs: $VAR1 = { '6' => { '0.1' => 0, '0' => 0 }, '5.3' => { '0.1' => 0, '0' => 0 }, '5.7' => { '0.1' => 0, '0' => 0 }, '5.1' => { '0.1' => 0, '0' => 0 }, '5.6' => { '0.1' => 0, '0' => 0 }, '5.8' => { '0.1' => 0, '0' => 0 }, '5.9' => { '0.1' => 0, '0' => 0 }, '5.5' => { '0.1' => 0, '0' => 0 }, '5.2' => { '0.1' => 0, '0' => 0 }, '5.4' => { '0.1' => 0, '0' => 0 }, '5' => { '0.1' => 0, '0' => 0 } };
Re: Is this a hash slice?
by tphyahoo (Vicar) on Mar 31, 2005 at 19:10 UTC
    It's all about the brackets. Where do you have to leave them out, where can you leave them in... and where does it work both ways?

    What I didn't realize going into this, is that this is a situation where it works both ways. The following demo clears things up to my satisfaction.

    Thanks, brother monks!

    use strict; use warnings; use Data::Dumper; #Populate the hash the first way. my @keys = qw(a b c d e f); my %hash; @hash{@keys} = (1,1,1,1,1,1); # first verse. print Dumper(\%hash); #clear %hash. for (keys %hash) { delete $hash{$_}; } print Dumper(\%hash); #Populate the hash the second way, using scalars and arrays. ${hash}{a} = 1; $hash{b} = 1; # proving brackets optional when setting hash values in +$calar mode. @{hash}{('c','d')} = (1,1); @hash{'e','f'} = (1,1); # proving brackets optional when setting hash +values in @rray mode # second verse, same as the first. print Dumper(\%hash);
      You never need to put braces around the hash name unless its name is wacky, and I can't figure out how to legally make one so wacky, so let's just ignore that possibility.

      You need braces around a hash reference any time it is not a simple scalar variable and you are not fetching a single element, because the precedence of the dereferencing operators will not DWYM otherwise. You may put braces around a hashref any time you feel like it. And those braces around the hashref are a true BLOCK: you can put any code you like in there, as long as the final result is a hash ref. It's just like a do-block, but the do is implied by the dereferencing.

      To illustrate the precedence considerations:

      my $foo = {a => 1}; # $foo is a hashref print $$foo{a}; # Ok print @$foo{a}; # one-element slice, but ok print $foo->{a}; # Preferred single-element fetch my %bar = (a => {b => 1}); # %bar is a HoH print ${$bar{a}}{b}; # Ok print @{$bar{a}}{b}; # one-element slice, but ok print @$bar{a}{b}; ### syntax error! print $bar{a}{b}; # Preferred single-element fetch
      The syntax error line thinks $bar must be a reference, like $foo was earlier. But it is $bar{a} that is the reference we want to dereference.

      See perldoc perlreftut for a nice statement of the (only 2!) rules for using references.


      Caution: Contents may have been coded under pressure.