perl - "our"或 "use vars"中有什么魔力满足 "use strict qw(vars)"?

标签 perl language-lawyer package variable-declaration scoping

我有工作代码,但我试图理解它为什么有效。我还试图了解有关 Perl 5 内部结构的更多信息(perlbrewperl-5.26.1Cygwin x64)。

我从perlvar知道和 strict 使用严格的“vars”通过在$^H中设置标志来实现。 Perl 然后根据这些标志测试对非 :: 变量的访问。不知何故,ouruse vars 都标记变量,以便它们通过测试。他们是如何做到的?

例如:

perl -E 'package Foo;
    use strict "vars";
    use vars qw($foo);
    say $foo;'

运行良好(尽管它不产生任何输出)。基于the source for use vars ,我尝试了这个,我认为会有同样的效果:

perl -E 'package Foo;
    use strict "vars";
    my $sym = "Foo::foo";          # <-- These two lines pulled straight
    *$sym = \$$sym;                # <-- from the source for the vars pragma
    say $foo;'

但是,它给了我一个错误:全局符号“$foo”需要显式包名称。我还在上面尝试了 $sym = "::Foo:foo" ,结果相同。

我查了一下,$Foo::foo 在符号表中:

$ perl -E 'package Foo;
      use Data::Dumper;
      use strict "vars"; 
      my $sym = "Foo::foo";
      *$sym = \$$sym; 
      say Dumper(\%{"Foo::"});'    # <-- Foo's symbol table

Output: 
$VAR1 = {
    'BEGIN'  => *Foo::BEGIN,
    'Dumper' => *Foo::Dumper,
    'foo'    => *Foo::foo          # <-- yep, it's there
};

use vars 还缺少什么? 我们的做同样的事情还是不同的事情?

更新

这是基于 melpomene's answer 的 A/B :

Fails                         Succeeds
-------------------------     ----------------------------------
package Foo;                  package Foo;
use strict "vars";            use strict "vars";

                              BEGIN {
                                  package Bar;
my $sym="Foo::foo";               my $sym = "Foo::foo";
*$sym = \$$sym;                   *$sym = \$$sym;
                              }
say $foo;                     say $foo;

最佳答案

use strict 'vars' works by setting flags in $^H.

是的,但这是一个实现细节。 $^H公开一些内部解释器状态位,但您不应该在正常代码中触及它。

Somehow, both our and use vars mark variables so that they will pass the test. How do they do so?

这也被视为实现细节。

<小时/>

但是,我们可以窥探一下幕后的情况。 strict "vars"提示未声明的变量(在编译时)。

有一个硬编码的变量列表可以免除此检查;它包括所有标点变量(例如 $/$_ 等以及 $a$b (由 sort 使用))。

所有词法(即本地)声明的变量也传递 strict ;就是这样my , our ,和state工作。 (就我们的目的而言, local 不是声明,也不会创建局部变量;local 临时更改现有变量的值。)

第三个异常(exception)是从模块导出的变量。使用全局变量作为模块接口(interface)的一部分通常被认为是一个坏主意,但一些较旧的模块仍然这样做。 English 还导出变量,因为这就是它的全部要点,所以我们将使用它作为示例:

use strict;
use English qw($INPUT_RECORD_SEPARATOR);
$INPUT_RECORD_SEPARATOR = "";  # <--
my $paragraph = readline STDIN;

标记为 <-- 的行不会抛出错误,因为 Perl 会记住哪些变量是从模块导入的。

“导出”到底是什么意思?它只是意味着跨包边界别名化符号:

*main::foo = \$Some::Module::foo;  # now $main::foo is an alias for $Some::Module::foo

奇怪的是,就 Perl 内部而言,如果变量在其他包中被别名,则该变量将被“导入”。它的别名是什么并不重要;别名;重要的是锯齿发生的位置。 use vars (ab-)使用此详细信息绕过 strict "vars"通过将您自己的变量导出回给您:

package Some::Package;
use vars qw($foo);

工作方式类似于

package Some::Package;
BEGIN {
    package vars;
    *Some::Package::foo = \$Some::Package::foo;
}
# now $foo is an alias to ... itself

另一个难题是 use发生在编译时,如 BEGIN block 。您的示例失败,因为您的别名尝试仅在运行时发生,这对于 strict 来说为时已晚。 ,并且因为它不会切换到不同的包来执行别名操作。

<小时/>

最后vars只是一个模块,用普通 Perl 编写。 our不同的是:它是真正的关键字,是语言的一部分。它还有不同的行为:它有效地创建一个别名(到包变量),但该别名位于本地范围内,而不是符号表中。

考虑例如以下内容:

my $foo = 2;
{
    our $foo = "hello";
    print "foo = $foo; main::foo = $main::foo\n";
}
print "foo = $foo; main::foo = $main::foo\n";

此输出

foo = hello; main::foo = hello
foo = 2; main::foo = hello

因为内心our $foo声明遮蔽了外部 $foo在内 block 中。在 block 内 $foo$main::foo引用同一个变量;外面$foo指的是词汇my $foo ,未受影响。

use vars 的另一个区别:

use strict;
package Foo;
our $x = "hello";
package Bar;
print "$x\n";  # hello

这段代码工作正常,因为 package声明不会创建新的范围。这里只有一个范围单元(整个文件),所以 our $x使$x引用$Foo::x对于文件的其余部分,无论您切换到哪个包。

另一方面:

use strict;
package Foo;
use vars qw($x);
$x = "hello";
package Bar;
print "$x\n";

这段代码甚至无法编译。引用$x最后一行中的内容无法解析:Perl 首先检查本地作用域,但没有本地声明的 $x的。然后它检查当前包( Bar ),也没有找到任何内容,并且没有 strict "vars" 。它会自动创建 $Bar::x为您服务,但与 strict "vars"启用这只是一个错误。 $Foo::x不相关且从未检查过。

关于perl - "our"或 "use vars"中有什么魔力满足 "use strict qw(vars)"?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49738776/

相关文章:

string - 如何将一行中的哈希引用转换为 Perl 中的常量

c++ - 为什么可以使用 operator<=> 比较数组成员,但不能使用独立数组?

java - 为什么用 javax.swing 而不是 java.swing?

perl - 有没有办法通过一些环境变量来调用 Perl 调试器?

regex - 如何制作一个perl单线 “line-endings agnostic”

perl - 使用自定义实用程序进行搜索和替换以进行转换

c++ - 为什么类外成员模板定义需要重复其声明 'requires-clause'

c++ - 我认为 N4140 §8.5[dcl.init]/17 中的要点 (17.6.2) 可能有一点不准确

c# - 自动将 PackageReferences 添加到 NuGet 包

没有包绑定(bind)的 Lisp 符号