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

Hello, I have this code snippet:
my %h = ('a', ['A', 'B'], 'b', 'B','a b', 'A B'); print "@{$h{a}}\n";
Why can't I say:
print "@${a}\n";

Also, why the curly braces surrounding the array (@{..})

Thanks - sue

Replies are listed 'Best First'.
Re: syntax issue
by hbm (Hermit) on Dec 27, 2011 at 18:23 UTC

    You can't print "@${a}\n" because you don't have an array reference named 'a'. This works:

    my $a = \@{$h{a}}; print "@${a}\n"; # or just "@$a\n";

    And the outer curlies in "@{$h{a}}\n" surround an array reference. Reading it inside-out, you have a hash key $h{a} whose value is an anonymous array. To print that value, you dereference it with @{}.

Re: syntax issue
by kennethk (Abbot) on Dec 27, 2011 at 18:28 UTC
    In the hash %h, the key a is associated with an array reference. Therefore, in order to access the values, the reference must be dereferenced using the @{...} syntax.

    One reason you can't say "@${a}\n" is because you forgot the letter h, so that gets read as interpolating the dereferenced variable $a into the string. If instead you consider what you likely meant to type, "@$h{a}\n", the order of operations is problematic -- the array dereference @ gets applied before the hash access, so perl is looking for a nonexistant scalar $h. See perlreftut.

    strict and warnings tells you as much for both these errors. Please read Use strict warnings and diagnostics or die.

Re: syntax issue
by graff (Chancellor) on Dec 27, 2011 at 19:02 UTC
    When curly braces follow a sigil ('@', '%' or '$'), the expression inside the braces is evaluated as a string, which is then treated as the name of a variable to be accessed according to the given sigil. I'm basing that on observed behavior -- I haven't found the particular manual that lays this out in detail; note the following one liner (which crucially does not 'use strict'):
    perl -le '%x=(y=>"a"); $a="Z";print ${$x{y}}'
    It creates a hash (%x) containing a single key/value pair ("y"/"a"); it also assigns a value of "Z" to variable $a (which happens to be a predefined global variable in perl). Then it uses the hash to look up the string "a", attaches the "$" sigil to that, and returns the value assigned to $a.

    If 'use strict' were in effect for the one-liner above, it would die with the error: "Can't use string ("a") as a SCALAR ref while "strict refs" in use at -e line 1." Of course, there are cases where curlies are useful, even with strictures in effect, for referring to chunks of data structures -- e.g.:

    use strict; my %hoa = ( foo => [0, 1], bar => [2, 3] ); for ( keys %hoa ) { print " $_ contains: @{$hoa{$_}}\n"; }

    It's not that you "can't say" something like @${a} (you can, provided you don't 'use strict') -- it's just that this doesn't do what you thought it would do. (What did you think it would do?) What it actually does is treat the predefined global variable "$a" as a reference to an array, and tries to dereference it so as to return the array. If 'strict refs' is in effect, this is an illegal operation; in the absence of 'strict refs', if $a was not assigned an array reference as its value, you'll just get (undefined) instead of an array.

Re: syntax issue
by JavaFan (Canon) on Dec 27, 2011 at 18:54 UTC
    why the curly braces surrounding the array
    Because that's the main syntax for dereferencing: sigil-block, with in most cases, the block requiring curlies (there are a few cases where the curlies can be omitted, or where an alternative arrow bases syntax is available -- but sigil-block always works). The result of the block should be a reference matching the sigil.

    So, in your case, the $h{a} gives you an array reference. To dereference it, use the sigil-block syntax, with $h{a} as the block content. So, the result is: @{$h{a}}.

    Note that you write @$h{a}, Perl considers this a short-hand for @{$h}{a}. This would try to (array)dereference $h, and to (hash)index the result, causing a run-time error.

Re: syntax issue
by TJPride (Pilgrim) on Dec 27, 2011 at 20:41 UTC
    use strict; use warnings; my %h = ('a', ['A', 'B'], 'b', 'B','a b', 'A B'); print join('-', keys %h) . "\n\n"; print join('-', values %h) . "\n\n"; print "@{$h{'a'}}\n\n"; print "$_ => $h{$_}\n" for keys %h; print "\n"; use Data::Dumper; print Dumper(\%h);

    Output:

    a-a b-b ARRAY(0x1801180)-A B-B A B a => ARRAY(0x1801180) a b => A B b => B $VAR1 = { 'a' => [ 'A', 'B' ], 'a b' => 'A B', 'b' => 'B' };

    Note that hashes do not retain order, so the elements may display in a different order from the order you defined them.

    Personally, I prefer this format if I'm creating a hash, just makes things a lot more clear:

    my %h = ( 'a' => ['A', 'B'], 'b' => 'B', 'a b' => 'A B' );

Re: syntax issue
by ikegami (Patriarch) on Dec 28, 2011 at 19:08 UTC

    Why can't I say: print "@${a}\n";

    I don't know. You should be able to, and I can.

    $ perl -wE'$a = [1,2,3]; say "@${a}";' 1 2 3

    What do you get? Perhaps $a didn't contain a reference to an array when you tried it?

    Also, why the curly braces surrounding the array (@{..})

    In general, they're optional. In @{ $h{a} }, they override precendence.

    @$h{a} => @{$h}{a} => A hash slice that expects $h to be a reference to a hash. @{ $h{a} } => @{$h}{a} => An array dereference that expects $h{a} to return an array ref.
Re: syntax issue
by Anonymous Monk on Dec 27, 2011 at 20:03 UTC
Re: syntax issue
by cjcollier (Novice) on Dec 28, 2011 at 18:20 UTC
    Hi there Sue! I'm not a core dev, so I don't know the why of not being able to use this syntax. We may be able to patch it so you *can* use that syntax, but it would be a bit of a bother, probably. Perl doesn't really have hard and fast rules about differences between types, so a hash could be seen as a list, and can be assigned from one to the other without the need of any type casting macros:
    $ cat /tmp/foo my %foo = ('a' => [ 'A','B' ], 'b' => 'B', 'a b' => 'A B' ); my @foo = %foo; my $foo = \@foo; print( "list: [@foo]\n", "listref deref: [@$foo]\n", ); __DATA__ list: [a ARRAY(0x1f12d48) a b A B b B] listref deref: [a ARRAY(0x1f12d48) a b A B b B]
    The @{...} around the reference is an explicit de-reference of the value between the { and }. Say you had "@$foo_bar", is this an attempt to de-reference $foo as an arrayref, followed by the string "_bar"? If so, you're doing it wrong, and it should instead be "@{$foo}_bar" Moo, C.J.
      nit: There's no such thing as a reference to a list in Perl. Your so-called listrefs are array references.