我有有效的代码,但是我试图理解它为什么起作用。我还试图了解有关Perl 5的更多内部信息(perlbrew
,perl-5.26.1
,Cygwin x64
)。
我从 perlvar
和 strict
知道use strict 'vars'
通过在$^H
中设置标志来工作。然后,Perl根据这些标志测试对非::
变量的访问。无论如何,our
和use 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;'
但是,它给了我一个错误:
Global symbol "$foo" requires explicit package name
。我在上面也尝试了$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
还有什么呢? our
是否做相同或不同的事情?更新
这是基于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;
最佳答案
是的,但这是一个实现细节。 $^H
公开了一些内部解释器状态位,但是您不应该在普通代码中接触它。
这也被视为实现细节。
但是,我们可以在后台进行窥视。 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
块一样在编译时发生。您的示例失败,因为您的别名尝试仅在运行时发生,对于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
。在该块中,$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/