http://www.perlmonks.org?node_id=1187140

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

One of the answers to Why does ‘keys’ need a named hash? made me remember a question that's been bugging me for awhile.

duelafn replied with

return keys %{ {map { $_ => 1 } @_} };

I've been using this syntax and have accepted it as idiomatic Perl, however, what I don't get is, why the curly braces? And why two pairs of them? Is there a reference dereference happening?

Given this:

say Dumper(\%{ {map { $_ => 'fish' } qw(one two red blue)} } ); ^ ^ ^ ^ |-|-----------------------------------------|-|
I get:
$VAR1 = { 'blue' => 'fish', 'one' => 'fish', 'red' => 'fish', 'two' => 'fish' };
If I remove the inner pair of indicated braces, I get
Ambiguous use of %{map{...}} resolved to %map{...}

So the inner pair is a code block. I'm not sure that clears anything up for me, unless I'm being thick ...
If I need to return an array, as opposed to a list or a hash, what would that syntax be?
So if I want this:

$VAR1 = [ 'one', 'fish', 'two', 'fish', 'red', 'fish', 'blue', 'fish' ];
How do I do it?

say Dumper(\@{ {map { $_ => 'fish' } qw(one two red blue)} } );
produces
Not an ARRAY reference
Which implies this is not a dereferencing thing ... unless of course I'm doing it wrong.

Thanks,
cbeckley

Replies are listed 'Best First'.
Re: Syntax for casting map to a hash or an array
by 1nickt (Abbot) on Apr 05, 2017 at 15:32 UTC

    The outer pair of curlies with the sigil dereferences the anonymous hash created by the inner pair, acting on the list returned by the block in the innermost pair, which is the argument to map. keys wants a hash.

    Also, Edit: Also, you likened "array" to:

    $VAR1 = [ 'one', 'fish', 'two', 'fish', 'red', 'fish', 'blue', 'fish' ];
    This is a scalar holding a reference to an anonymous array. An array is differentiated from a list by context, not by square versus round brackets.

    But for your last question, if you want an array containing the flattened hash derived from your map and then want to dumper it to get the output you showed:

    say Dumper( [ map { $_ => "fish" } qw(one two red blue) ] );
    Because square brackets return a reference to what map returns, so Data::Dumper prints as one data structure.


    The way forward always starts with a minimal test.

      Aaaaah. I get it.

      So this is how to get the array:

      say Dumper(\@{ [map { $_ => 'fish' } qw(one two red blue)] } );

      I had tried

      say Dumper(\@[ {map { $_ => 'fish' } qw(one two red blue)} ] );
      after having half convinced my self the inner braces were a code block, but of course that didn't work either ...

      Thank you very much.

      Thanks,
      cbeckley

        Although it is technically a block, it's really an operator. %{} and @{} are often referred to as the "circumfix operators". They are available wholly to dereference what is within the block to whatever sigil is placed before it.

        \@{ [map { $_ => 'fish' } qw(one two red blue)] }

        Well, that works but it's a bit wasteful: first you're creating a arrayref with [], then you're dereferencing it with @{}, and then taking a reference to that again with \. It's easier to just use one set of []'s like 1nickt showed (although you may not have seen that yet since it was part of a large ninja edit).

        Nope, one level too many of referencing / deferencing. See my example above.

        With this kind of thing it's best to strip it down to the innermost element and print its output with a data dumper. Then when you understand what is going on, add a layer, repeat. Trying to get at it from the outer edge when you are confused is a bit like flailing. A methodical approach is most effective.


        The way forward always starts with a minimal test.
Re: Syntax for casting map to a hash or an array
by haukex (Bishop) on Apr 05, 2017 at 15:50 UTC
    Is there a reference dereference happening?

    Yes - the curlies directly around the map create a hash reference from the list that map returns (see number 3 in Making References), and then the outer %{ } dereferences that hash reference (see number 2 in Using References).

    So the inner pair is a code block.

    Nope, it's a hashref constructor, but since it can also look like a block, Perl sometimes has to "guess" which one it is, and AFAIK that's where the warning is coming from.

    If I need to return an array, as opposed to a list or a hash, what would that syntax be?

    The same kind of reference-dereference operation, just in this case create an arrayref with [...] and dereference it with @{...}:

    print Dumper @{ [ map {"<$_>"} qw/a b c/ ] }; __END__ $VAR1 = '<a>'; $VAR2 = '<b>'; $VAR3 = '<c>';

    Although I think this is needed less often than the %{{...}} trick when it comes to passing stuff to functions, since most functions impose list context on their arguments anyway. However, it is sometimes used as a trick to interpolate things into strings that don't normally interpolate, so for example:

    sub foo { return "World!" } print "Hello, ", foo(), "\n"; # prints "Hello, World!" print "Hello, foo()!\n"; # prints "Hello, foo()!" print "Hello, @{[ foo() ]}\n"; # prints "Hello, World!"

    While in this example the first print looks cleaner than the last, the trick can sometimes be useful when interpolating things into long heredocs, for example.

      Thank you again. A lot of information as always.

      It makes sense that there wouldn't be too many @{[...]]} running around in the wild.

      Thanks in particular for this:

      print "Hello, @{[ foo() ]}\n"; # prints "Hello, World!"

      Thanks,
      cbeckley