10
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'

没有输出。

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

4

1 回答 1

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值,例如,当您在右值上下文中访问未初始化的数组索引时返回。在 Perl API 中,这是&PL_sv_undef. 您可以获得对该值的引用,例如\undef,并且可以为它取一个变量的别名。

访问哈希值的两种方式

在内部,使用hv_fetchor获取哈希条目hv_fetch_ent。作为参数,两者都采用散列、密钥和标志,告诉它们访问是否是只读的。

如果这是一个只读访问并且该元素不存在,则将返回一个空指针,它undef在 Perl 空间中显示为该值。此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表示左值/读写访问。

为什么这种行为“有意义”</h3>

for-loops 中,$_成为当前元素的别名可能非常有用。在mapgrep中,这是一种避免复制整个标量的性能技巧。别名要便宜得多,因为这仅意味着单个指针的副本。

于 2013-11-07T11:21:04.497 回答