8

为什么以下代码段完全有效?使用它可能会带来什么邪恶?但说真的,是否有任何理由,代码${}完全被评估然后用作标量引用?

use strict;
no strict 'refs';

our $message = "Hello world!";
print "${ lc 'MESSAGE' }\n";
4

3 回答 3

13

我们在Intermediate Perl中对此进行了深入的解释。

变量查找的一般语法是:

 SIGIL  BLOCK  INDEXY-THING

对于一个看起来像这样的简单标量:

 print $   { foo };

当您需要将变量名与其周围的事物分开时,您可能已经看到了这一点:

 print "abc${foo}def\n";

如果你在块中只有一个 Perl 标识符并且周围没有混乱,你可以去掉大括号,这是常见的情况:

 print $foo;

但是,这与取消引用引用相同:

 SIGIL  BLOCK-RETURNING-REFERENCE  INDEXY-THINGS

如果你在块中得到的东西是一个引用,Perl 会尝试像你问的那样取消引用它:

 my $ref = \ '12345';
 print $     { $ref };

这是一个真正的障碍,而不仅仅是糖。您可以在其中包含任意数量的语句:

 print $     { my $ref = \ '1234'; $ref };

现在您不只是指定一个 Perl 标识符,因此 Perl 不会假定您给它一个标识符,它会执行代码并将结果用作参考。say考虑一下这些几乎相同的陈述之间的区别:

    use 5.010;
our $foo = "I'm the scalar";

sub foo { \ "I'm the sub" }

say ${foo};
say ${foo;};

在那一刻, sayPerl 看到分号,意识到它不是标识符,将大括号内的代码解释为文本,并返回结果。由于结果是一个引用,它使用${...}来取消引用它。在哪里执行此操作并不重要,因此在双引号字符串中执行此操作并不特殊。

另外,请注意our那里。现在这很重要,因为您将考虑一些更棘手的事情:

    use 5.010;
our $foo = "I'm the scalar";

sub foo { \ "I'm the sub" }
sub baz { 'foo' }

say ${foo};
say ${foo;};
say ${baz;};

Perl 将其解释say为代码,并认为结果不是参考;这是简单的字符串foo。Perl 发现它不是引用,但它现在处于取消引用上下文中,因此它执行符号引用(如Greg Bacon 所述)。由于符号引用与符号表中的变量一起使用,因此它$foo必须是包变量。

由于这很容易搞砸,所以strict有一个方便的检查。但是,当您将其关闭时,当它咬您时不要感到惊讶。:)

于 2010-02-25T23:09:31.077 回答
5

perlref 文档的“使用参考”部分

将标识符(或标识符链)作为变量或子例程名称的一部分放在任何地方,都可以将标识符替换为 BLOCK 返回正确类型的引用。换句话说,前面的例子可以这样写:

$bar = ${$scalarref};
push(@{$arrayref}, $filename);
${$arrayref}[0] = "January";
${$hashref}{"KEY"} = "VALUE";
&{$coderef}(1,2,3);
$globref->print("output\n");  # iff IO::Handle is loaded

诚然,在这种情况下使用花括号有点傻,但是 BLOCK 可以包含任意表达式,尤其是下标表达式:

&{ $dispatch{$index} }(1,2,3);    # call correct routine

由于能够为 的简单大小写省略花括号$$x,人们经常错误地将解引用符号视为正确的运算符,并怀疑它们的优先级。但是,如果它们是,您可以使用括号而不是大括号。事实并非如此。考虑以下差异;案例 0 是案例 1 的简写版本,而不是案例 2:

$$hashref{"KEY"}   = "VALUE";     # CASE 0
${$hashref}{"KEY"} = "VALUE";     # CASE 1
${$hashref{"KEY"}} = "VALUE";     # CASE 2
${$hashref->{"KEY"}} = "VALUE";   # CASE 3

案例 2 也具有欺骗性,因为您正在访问一个名为 的变量%hashref,而不是通过$hashref它可能引用的哈希来取消引用。那将是案例3。

稍后在“符号引用”中:

我们说过,如果引用未定义,则它们会在必要时出现,但我们没有说明如果用作引用的值已经定义但不是硬引用会发生什么。如果您将其用作引用,它将被视为符号引用。也就是说,标量的值被视为变量的名称,而不是与(可能)匿名值的直接链接。

于 2010-02-25T11:39:02.440 回答
4

没关系,除非您使用符号引用。假设以下代码:

my %messages = (hello => "Hello world!", bye => "Bye-bye, world!");
sub get_message_ref { return \$messages{$_[0]} }; # returns scalarref
print "${ get_message_ref('bye') }\n";

同意,它对 scalarrefs 的用处并不明显,但它对 arrayrefs 非常有用。

print "keys: @{[keys %messages]}\n";
于 2010-02-25T09:29:37.300 回答