散列键行为

时间:2013-11-07 09:26:00

标签: perl autovivification

perl -Mstrict -wlE 'my %h; say grep 0, $h{poluted}; say keys %h'

输出

poluted

perl -Mstrict -wlE 'my %h; say grep 0, my @r= $h{poluted}; say keys %h'

没有输出。

我想知道为什么输出会有所不同?

1 个答案:

答案 0 :(得分:13)

混叠

在Perl的循环结构mapgrepfor中,$_变量别名到每个当前项。虽然$_可能是只读的,但它始终代表有效的标量值。

例如,以下代码将死:

$_ = 1 for 1, 2, 3;  # constants are read-only

但这有效:

my @nums = (1, 2, 3);
$_ = 1 for @nums;  # @nums isn't read-only

请注意,分配执行副本,但别名将名称与现有标量相关联。

两个undef

Perl有两种​​undef

  • 标量可以设置为代表undef。例如:

    my $foo;  # is this kind of undef
    $foo = 1; # isn't undef any more
    
  • 一个特殊的全球唯一标量,代表只读undef值,例如在rvalue上下文中访问未初始化的数组索引时返回。在Perl API中,这是&PL_sv_undef。您可以获得对此值的引用,例如\undef,可以别名变量。

访问哈希值的两种方法

在内部,使用hv_fetchhv_fetch_ent提取哈希条目。作为参数,两者都使用哈希,密钥和标志来告诉他们访问是否是只读的。

如果这是只读访问且元素不存在,则返回空指针,该指针在Perl空间中显示为undef值。此undef值未与哈希值相关联。因此,not exists $hash{foo}暗示not defined $hash{foo}

但如果它不是只读且元素不存在,则会创建一个新条目,然后返回该条目。但是,此条目最初为undef,直到通过分配将其设置为其他值。

那么为什么问题中的代码不能按预期工作呢?

grep 0, $h{polluted}

循环结构的参数列表别名为$_。如果列表中的表达式是常量或子程序,那么就不会发生任何惊人的事情。但是当它们是变量访问时,则意味着读写访问。

因此,为了获得$h{polluted}的值,Perl显然在读写模式下进行访问。如果我们查看这个表达式的操作码,我们实际上看到了:

3  <0> pushmark s
4  <#> gv[*h] s
5  <1> rv2hv sKR/1
6  <$> const[PV "polluted"] s/BARE
7  <2> helem sKM/2                # <-- hash element access, "M" flag is set!
8  <@> grepstart K
9  <|> grepwhile(other->a)[t2] vK
a      <$> const[IV 0] s
           goto 9

M代表MOD,这意味着左值/读写访问。

为什么这种行为会“有意义”

for - 循环中,让$_成为当前元素的别名可能非常有用。在mapgrep中,这是一个避免整个标量副本的性能黑客攻击。别名更便宜,因为这只意味着单个指针的副本。