"no autovivication" pragma fails with grep in Perl
It's not grep
but the aliasing of the values in your hash slice that causes this.
This has the same effect:
for (@h{@fake_keys}){
my $t = $_;
}
You can work around it by copying the slice:
use strict;
use warnings FATAL => 'all';
use feature 'say';
use autodie ':all';
use DDP;
my %h = (
'a' => 1,
'b' => 2,
);
p %h; # pretty print the original hash
my @fake_keys = ('x', 'y', 'z');
if (grep {defined} @{[@h{@fake_keys}]}) {
say "found fake keys";
}
p %h; # show that 'x', 'y', & 'z' are added as undef keys
Or access each value in the grep block like shown by Ikegami.
Solution:
if (grep { defined($h{$_}) } @fake_keys) {
say "found fake keys";
}
Explanation follows.
Autovivification, as used in the Perl documentation, is the creation of anon vars and references to them when an undefined scalar is dereferenced.
For example, autovivification dictates that $x->[0]
is equivalent to ( $x //= [] )->[0]
.
For example, autovivification dictates that $h{p}{q}
is equivalent to ( $h{p} //= {} )->{q}
.
There is no dereferencing in your code, so autovivifaction couldn't possibly occur in your code, so no autovivification;
doesn't help.
What you have in your code is a hash element used as an lvalue. "lvalue" means assignable value. It's named after the fact that such expressions are usually found on the left of assignments.
$h{key} = ...;
# ^^^^^^^
# lvalue
But they are also found elsewhere in Perl.
for ($h{key}) {
# ^^^^^^^
# lvalue
}
map { } $h{key}
# ^^^^^^^
# lvalue
grep { } $h{key}
# ^^^^^^^
# lvalue
some_sub($h{key});
# ^^^^^^^
# lvalue
This is because the block of each of looping constructs can modify the items being processed by modifying $_
, and subs can modify their arguments my modifying the elements of @_
.
for ($h{key}) {
$_ = uc($_); # Modifies $h{key}
}
grep { $_ = uc($_) } $h{key} # Modifies $h{key} # Bad practice, but possible.
map { $_ = uc($_) } $h{key} # Modifies $h{key}
sub some_sub {
$_[0] = uc($_[0]); # Modifies $h{key}
}
some_sub($h{$k});
For that to be possible, $h{$k}
must exist before the loop body is entered or the sub is called.
$ perl -M5.010 -e'for ($h{key}) { } say 0+keys(%h);'
1
Sub calls use expensive magic to avoid this.
$ perl -M5.010 -e'sub f { } f($h{key}); say 0+keys(%h);'
0
But grep
and map
do not.
$ perl -M5.010 -e'grep { 1 } $h{key}; say 0+keys(%h);'
1
$ perl -M5.010 -e'map {; } $h{key}; say 0+keys(%h);'
1