简体   繁体   中英

Why does Perl treat a hash element as list context at declaration?

Given this code:

#!/usr/bin/perl -w

use strict;
use warnings;

sub foo {
    return wantarray ? () : "value1";
}

my $hash = {
    key1 => foo(),
    key2 => 'value2'
};

use Data::Dumper;
print Dumper($hash);

I get the following output:

$VAR1 = {
  'key1' => 'key2',
  'value2' => undef
};

When I would expect:

$VAR1 = {
  'key1' => 'value1',
  'key2' => 'value2'
};

I understand that a hash is kind-of an even-sized array (as evidenced by the "Odd number of elements in hash assignment" warning I'm getting) but a hash element can only be a scalar, why would the compiler be giving it array context?

I found this using the param function of the CGI module when assigning directly to a hash. The foo() function above was a call to CGI::param('mistyped_url_param') which was returning an empty array, destroying (rotating?) the hash structure.

The fat comma isn't a special hash assignment operator. It just a piece of syntactic sugar that means "auto-quote the previous thing"

So:

my $hash = {
    key1 => foo(),
    key2 => 'value2'
};

Means:

my $hash = {
    'key1', foo(), 'key2', 'value2'
};

… which is a list and, as willert says:

every expression in a list is evaluated in list context. You can get around this by calling scalar foo()

An anonymous hash constructor supplies list context to the things inside it because it expects a list of keys and values. It's that way because that's the way it is. We don't have a way to represent a Perl hash in code, so you have to use a list where we alternate keys and values. The => notation helps us visually, but performs no magic that helps Perl figure out hashy sorts of things.

Current context propogates to subroutine calls, etc just like it does in any other situation.

This allows you to build hashes with list operations:

 my $hash = { 
      a => 'A',  
      map( uc, 'd' .. 'f' ),
      return_list( @args ), 
      z => 'Z' 
      };

If you need something to be in scalar context, just say so using scalar :

 my $hash = { 
      a => 'A',  
      map( uc, 'd' .. 'f' ),
      'g' => scalar return_item( @args ), 
      z => 'Z' 
      };

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM