"no autovivication" 在 Perl 中 pragma 使用 grep 失败

"no autovivication" pragma fails with grep in Perl

我正在尝试使用模块关闭自动激活: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

或者像 Ikegami 的 那样访问 grep 块中的每个值。

解决方案:

if (grep { defined($h{$_}) } @fake_keys) {
   say "found fake keys";
}

解释如下。


Perl 文档中使用的自动生成是在取消引用未定义的标量时创建匿名变量和对它们的引用。

例如,自动生成表明 $x->[0] 等同于 ( $x //= [] )->[0]

例如,自动生成表明 $h{p}{q} 等同于 ( $h{p} //= {} )->{q}

您的代码中没有取消引用,因此您的代码中不可能发生自动生成,因此 no autovivification; 没有帮助。


您的代码中有一个用作左值的散列元素。 “左值”表示可分配的值。它的名字来源于这样的表达式通常在作业的 left 中找到。

  $h{key} = ...;
# ^^^^^^^
# lvalue

但在 Perl 的其他地方也可以找到它们。

for ($h{key}) {
   # ^^^^^^^
   # lvalue
}
map { } $h{key}
      # ^^^^^^^
      # lvalue
grep { } $h{key}
       # ^^^^^^^
       # lvalue
some_sub($h{key});
       # ^^^^^^^
       # lvalue

这是因为每个循环构造的块可以通过修改$_来修改正在处理的项目,而subs可以通过修改@_.[=34=的元素来修改它们的参数。 ]

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});

要做到这一点,$h{$k} 必须在进入循环体或调用子程序之前存在。

$ perl -M5.010 -e'for ($h{key}) { }  say 0+keys(%h);'
1

子调用使用昂贵的魔法来避免这种情况。

$ perl -M5.010 -e'sub f { }  f($h{key});  say 0+keys(%h);'
0

但是 grepmap 没有。

$ 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