Hashref assignment consumes next key instead of assigning undef

75 views Asked by At

I'm trying to initialize a hashref containing the results of several expressions. I would expect expressions that return undefined results to assign undef to the appropriate key. Instead, the assignment just gobbles up the next key as though the expression was never there to begin with.

A quick example is probably easier to understand:

use Data::Dumper;
my $str = "vs";
my $contains = {
    t => ($str =~ /t/i),
    u => ($str =~ /u/i),
    v => ($str =~ /v/i),
};
print(Data::Dumper->Dump([$contains]));

I would expect the code above to print:

$VAR1 = {
    'v' => 1,
    't' => undef,
    'u' => undef
};

Instead, I get the following:

$VAR1 = {
    't' => 'u',
    'v' => 1
};

Adding an explicit undef to the assignment does get me the result I'm looking for:

use Data::Dumper;
my $str = "vs";
my $contains = {
    t => ($str =~ /t/i || undef),
    u => ($str =~ /u/i || undef),
    v => ($str =~ /v/i || undef),
};
print(Data::Dumper->Dump([$contains]));

However, this seems a little counterintuitive to me. Can anybody explain this behavior?

1

There are 1 answers

0
amon On BEST ANSWER

You're running into the problem of list context vs scalar context. To get your desired output, you must force the match to be in scalar context, e.g. like scalar($str =~ /t/i), or ($str =~ /t/i) || undef if you really want undef in case the match fails.

A regex match /.../ behaves differently in different contexts:

  • In scalar context, it returns a true/false value depending on whether it matches
  • In list context, behaviour is more complicated:
    • If you have any capture groups, successful match returns the values of those captures.
    • If you have no capture groups, successful match returns a list with the single element 1.
    • Unsuccessful match returns the empty list.

Here, you have no capture groups so your regex matches evaluate to the empty list if they don't match – not undef. So the hashref construct actually sees these values:

my $contains = {
    't',
    'u',
    'v', 1,
};

The fat-arrow => operator does not create key–value pairs, it is just a variant of the comma operator that turns the key on the left side into a string.