#!/usr/bin/env perl use strict; use warnings; our $x; sub print_func { print "$x\n"; } for $x (1 .. 10) { print_func; }
打印1到10像你所期望的,但以下不:
#!/usr/bin/env perl use strict; use warnings; my $x; sub print_func { print "$x\n"; } for $x (1 .. 10) { print_func; }
发出以下警告10次:
Use of uninitialized value $x in concatenation (.) or string at perl-scoping.pl line 8.
这里发生了什么?我知道Perl子程序不能嵌套(并且始终具有模块范围),因此它们似乎无法关闭我的变量.看来,在这种情况下,严格模式下的perl应该使用以下消息拒绝第二个程序:
Global symbol "$x" requires explicit package name at perl-scoping.pl line 6. Global symbol "$x" requires explicit package name at perl-scoping.pl line 9.
即它应该拒绝子例程,因为自由变量没有被声明为任何地方,而for循环因为变量尚未被声明.
为什么Perl以这种方式行事?
解决方法
If the variable is preceded with the keyword
my
,then it is lexically scoped,and is therefore visible only within the loop. Otherwise,the variable is implicitly local to the loop and regains its former value upon exiting the loop. If the variable was prevIoUsly declared withmy
,it uses that variable instead of the global one,but it’s still localized to the loop.
换句话说,循环迭代器总是被定位到循环中.如果它是全局的,那么它的行为就像在循环块中被声明为本地的.如果它是一个词汇,那么它的行为就像它在循环块中被声明的那样.
将这个应用于您的两个示例将有助于了解发生了什么.
our $x; sub print_func { print "$x\n"; } for $x (1 .. 10) { print_func; }
在该循环中有一个隐式的本地$x. local
真的应该被命名为temp.它在其范围内暂时覆盖全局变量的值,但它仍然是全局变量.这就是为什么print_func可以看到它.
当其范围结束时恢复旧值.如果在for循环之后添加print $x,可以看到这一点.
use v5.10; our $x = 42; for $x (1 .. 10) { say $x; } say $x; # 42
我们来看看你的代码涉及到词汇(我的变量).
my $x; sub print_func { print "$x\n"; } for $x (1 .. 10) { print_func; }
这里真正发生的是你有两个词汇变量,称为$x.一个是文件范围,一个是范围的循环. for循环中的内部$x先于外部$x.这被称为“阴影”.
在物理范围之外不能看到词汇. print_func()只看到外部未初始化的$x.
这里有一些风格的外卖.
始终将参数传递到您的函数中.
实际上,print_func应该有一个参数.那么你不用担心复杂的范围规则.
sub print_func { my $arg = shift; print "$arg\n"; } for $x (1..10) { print_func($x); }
总是用于我的$x.
不要依赖于复杂的隐式for循环范围规则.总是用我的声明循环迭代器.
for my $x (1..10) { print_func($x); }
避免全局变量
由于很难说出访问全球的内容,不要使用它们.如果你认为你需要一个全局的,写一个函数来控制对一个文件范围的词法的访问.
my $Thing = 42; sub get_thing { return $Thing } sub set_thing { $Thing = shift; return }
声明您的变量接近他们使用的位置.
Ye olde编码风格将做一些事情,例如将所有变量声明在文件或函数的顶部.这是一个非常非常非常古老的语言,它要求只在某些地方声明变量. Perl和大多数现代语言没有这样的限制.
如果您一次性声明您的变量,就很难知道它们的内容,很难知道使用或影响的变量.如果你声明它接近于它的第一次使用,限制什么可以影响它,并使它更明显它是什么.