为什么 Perl 在字符串插值期间计算 ${…} 中的代码?
-
22-09-2019 - |
题
为什么下面的代码片段能起作用?使用这个可能会带来什么邪恶呢?但说真的,有什么原因吗,代码中 ${}
完全被评估然后用作标量参考?
use strict;
no strict 'refs';
our $message = "Hello world!";
print "${ lc 'MESSAGE' }\n";
解决方案
没关系, 除非你使用符号引用. 。假设有以下代码:
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";
其他提示
我们深入解释了这一点 中级 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;};
在那一秒 say
Perl 看到分号,意识到它不是标识符,将大括号内的代码解释为文本,并返回结果。由于结果是参考,因此它使用 ${...}
取消引用它。在哪里执行此操作并不重要,因此在双引号字符串内执行此操作并不特殊。
另外,请注意 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 发现它不是一个引用,但它现在处于解引用上下文中,因此它执行符号引用(如 格雷格·培根描述)。由于符号引用使用符号表中的变量,因此 $foo
必须是一个包变量。
因为很容易搞砸 strict
有一个方便的检查。然而,当你把它关掉时,当它咬你时不要感到惊讶。:)
从 “使用参考” 的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
诚然,这是一个有点傻使用花括号在这种情况下,但块可以包含任意的表达,特别是,下标表达式:
由于能够省略花括号为&{ $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。
后来在 “符号引用”:
我们说,引用弹入存在的必要,如果他们是不确定的,但我们并没有说,如果作为一个参考值已定义发生了什么,但不是硬引用。如果你使用它作为一个参考,它会被当作一个符号引用。也就是说,标量的值被取为变量的名称,而不是直接连接到一个(可能)匿名值。