为什么Perl在字符串插值期间评估$ {...}中的代码?

wil*_*ert 6 perl symbolic-references

为什么以下代码段可以正常工作?使用这个可能有什么邪恶?但是说真的,有什么理由,根本${}得到的代码被评估然后用作标量参考?

use strict;
no strict 'refs';

our $message = "Hello world!";
print "${ lc 'MESSAGE' }\n";
Run Code Online (Sandbox Code Playgroud)

bri*_*foy 12

我们在Intermediate Perl中深入解释了这一点.

变量查找的一般语法是:

 SIGIL  BLOCK  INDEXY-THING
Run Code Online (Sandbox Code Playgroud)

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

 print $   { foo };
Run Code Online (Sandbox Code Playgroud)

当您需要将变量名称与其周围的事物分开时,您可能已经看到过这种情况:

 print "abc${foo}def\n";
Run Code Online (Sandbox Code Playgroud)

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

 print $foo;
Run Code Online (Sandbox Code Playgroud)

但是,取消引用引用是一回事:

 SIGIL  BLOCK-RETURNING-REFERENCE  INDEXY-THINGS
Run Code Online (Sandbox Code Playgroud)

如果您在块中获得的内容是引用,Perl会尝试取消引用它,就像您提出的那样:

 my $ref = \ '12345';
 print $     { $ref };
Run Code Online (Sandbox Code Playgroud)

这是一个真正的障碍,而不仅仅是糖.您可以在那里拥有任意数量的语句:

 print $     { my $ref = \ '1234'; $ref };
Run Code Online (Sandbox Code Playgroud)

现在你不只是指定一个Perl标识符,所以Perl不会假设你给它一个标识符并且它执行代码并使用结果作为参考.考虑这些几乎相同的say陈述之间的区别:

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

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

say ${foo};
say ${foo;};
Run Code Online (Sandbox Code Playgroud)

在第二个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;};
Run Code Online (Sandbox Code Playgroud)

Perl int代表最后say作为代码并且看到结果不是引用; 这是简单的字符串foo.Perl认为它不是一个参考,但它现在处于一个解除引用的上下文中,所以它做了一个符号引用(正如Greg Bacon描述的那样).由于符号引用使用符号表中的变量,因此$foo必须是包变量.

因为它很容易搞砸了,所以strict有一个方便的检查.但是,当你把它关掉的时候,当它咬你时不要感到惊讶.:)


Gre*_*con 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
Run Code Online (Sandbox Code Playgroud)

不可否认,在这种情况下使用curlies有点傻,但BLOCK可以包含任意表达式,特别是下标表达式:

&{ $dispatch{$index} }(1,2,3);    # call correct routine
Run Code Online (Sandbox Code Playgroud)

由于能够在简单的情况下省略curlies $$x,人们常常会错误地将解除引用符号视为正确的操作符,并且想知道它们的优先级.但是,如果它们是,您可以使用括号而不是括号.事实并非如此.考虑下面的差异; case 0是案例1的简写版本,而不是案例2:

$$hashref{"KEY"}   = "VALUE";     # CASE 0
${$hashref}{"KEY"} = "VALUE";     # CASE 1
${$hashref{"KEY"}} = "VALUE";     # CASE 2
${$hashref->{"KEY"}} = "VALUE";   # CASE 3
Run Code Online (Sandbox Code Playgroud)

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

后来在"符号引用"中:

我们说如果它们是未定义的,那么引用会根据需要弹出,但我们没有说明如果已经定义了用作引用的值,但不是硬引用会发生什么.如果您将其用作参考,则将其视为符号引用.也就是说,标量的值被视为变量的名称,而不是指向(可能)匿名值的直接链接.


cod*_*lic 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";
Run Code Online (Sandbox Code Playgroud)

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

print "keys: @{[keys %messages]}\n";
Run Code Online (Sandbox Code Playgroud)