Perl中的grep导致“noautovivication”编译指示失败
我正在尝试使用以下模块关闭自动激活:https ://metacpan.org/pod/autovivification但 grep 失败:
#!/usr/bin/env perl
use strict;
use warnings FATAL => 'all';
use feature 'say';
use autodie ':all';
use DDP;
no autovivification;
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
如何关闭grep的自动激活?
回答
grep导致这种情况的不仅仅是散列切片中值的别名。
这具有相同的效果:
for (@h{@fake_keys}){
my $t = $_;
}
您可以通过复制切片来解决它:
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
或者访问 grep 块中的每个值,如Ikegami所示。
回答
解决方案:
if (grep { defined($h{$_}) } @fake_keys) {
say "found fake keys";
}
解释如下。
Perl 文档中使用的自动激活是在取消引用未定义的标量时创建匿名变量和对它们的引用。
例如,autovivification 规定它$x->[0]相当于( $x //= [] )->[0]。
例如,autovivification 规定它$h{p}{q}相当于( $h{p} //= {} )->{q}。
您的代码中没有取消引用,因此您的代码中不可能发生自动活体,因此no autovivification;无济于事。
您在代码中拥有的是用作左值的哈希元素。“左值”表示可分配的值。这是事实,这样的表述通常在发现而得名升作业的左边。
if (grep { defined($h{$_}) } @fake_keys) {
say "found fake keys";
}
但是它们也可以在 Perl 的其他地方找到。
$h{key} = ...;
# ^^^^^^^
# lvalue
for ($h{key}) {
# ^^^^^^^
# lvalue
}
map { } $h{key}
# ^^^^^^^
# lvalue
grep { } $h{key}
# ^^^^^^^
# lvalue
这是因为每个循环结构的块都可以通过修改来修改正在处理的项目$_,而 subs 可以通过修改@_.
some_sub($h{key});
# ^^^^^^^
# lvalue
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}
为此,$h{$k}必须在进入循环体或调用 sub 之前存在。
sub some_sub {
$_[0] = uc($_[0]); # Modifies $h{key}
}
some_sub($h{$k});
子调用使用昂贵的魔法来避免这种情况。
$ perl -M5.010 -e'for ($h{key}) { } say 0+keys(%h);'
1
但是grep,map不要。
$ perl -M5.010 -e'sub f { } f($h{key}); say 0+keys(%h);'
0
$ perl -M5.010 -e'grep { 1 } $h{key}; say 0+keys(%h);'
1