perl 变量 my our local state
perl 变量类型与作用域
根据作用域的不同来区分,perl有两种变量类型:
词法变量(Lexical Variable) -- 词法作用域,只在当前词法作用域内可见。
包变量(Package Variable) -- 全局变量,在任何位置可见
有三种创建变量的方式:
my -- 创建一个词法变量
our -- 创建一个词法变量,但实际上是包变量的别名,或者说是伪装成词法变量的包变量
直接使用 -- 不加任何声明的使用一个变量,将创建一个包变量
下面我们详细说一下这几种方式
直接使用 -- 包变量
$foo = 1;
在perl早期的时候,大家都是这么定义,使用变量的。
这么定义变量$foo将会是全局变量,也就是在程序的任何位置都能访问到它。
这样当随着代码规模的增长,会引起一个严重的问题,变量污染,比如:
$foo = 1; # $foo -> 1
f();
# $foo -> 2
sub f {
$foo = 2;
}
如果不小心命名,则很容易在某个意想不到的位置,变量$foo被赋值,出现bug。
所以 perl5 引进了strict 和warnings 两个pragma,在代码上加上use strict后,所有没有
直接声明而使用的变量都会报错。
所以现在这种用法已经废弃了,在一些教程书中会这么开始教学,但这种用法在实际代码中是绝对不建议再使用的。
取而代之的是 my
my -- 词法变量
my $foo = 1;
my 的出现,极大的解决了变量污染的问题,因为my所声明的变量是 -- 局部变量,词法作用域。
局部变量的概念,比较好理解,比如:
my $foo = 1; # $foo -> 1
f();
# $foo -> 1
sub f {
my $foo = 2 # $foo -> 2
}
在函数f中的$foo 之在局部范围内起作用,出了函数之后,就不再起作用,所以也就不会影响到外部的$foo
需要理解的是这里的词法作用域,词法作用域限制了局部变量的名字生存的范围,这里一个词法作用域,基本上就是一个BLOCK,也就是一个花括号阔住的范围。
所以,只要有一个BLOCK就会建立一个词法作用域,比如:
my $foo = 1; # $foo -> 1
f();
# $foo -> 1
sub f {
my $foo = 2 # $foo -> 2
if ( 1 ) {
my $foo = 3 # $foo -> 3
}
}
这里在函数f内部的if语句中,建立一个新的词法作用域,同样对这里的$foo赋值不会影响到外部的$foo
my基本上已经够用了,然而由于之前提到的strict 和warnings ,导致不能使用全局变量。
因此出现了our
our -- 伪装成词法变量的包变量
our $foo = 1;
our 的出现解决了不能使用全局变量的问题,our所声明的变量是全局变量的词法作用域的别名。
意思就是,这里$foo是$main::foo的别名,如果是在模块MyModule中声明的话,就是$MyModule::foo.
但是它同时又是词法作用域的,所以在词法作用域范围外无法访问到它,只能通过模块变量名$MyModule::foo来访问
可以说 our 所声明的变量是伪装成词法变量的全局变量。
举个例子:
package A;
{
our $foo = 1;
print $foo; # 1
}
print $foo; # error
print $A::foo; # 1
package B;
print $A::foo; # 1
这里在花括号之外将无法访问到$foo, 但能通过模块变量名来访问 $A::foo
在模块B中,也能通过$A::foo来访问模块A所定义的变量。
local -- 包变量的临时赋值
local $foo = 1;
local 并不创建一个新的变量,而是对已有的包变量临时赋予一个值,在退出当前scope后将原来的值还回去。
$foo = 1; # $foo -> 1
{
local $foo = 2; # $foo -> 2 将原先的$foo藏起来,赋予一个临时的值
}
$foo # $foo -> 1 将藏起来的值还回去
看起来有点像my,local确实是在my出现以前的解决方案,在my出来之后,使用的情形也就不多了。
比较有意思的是,local是动态作用域,什么意思呢? 我们比较一下下面两个代码:
my $foo = 1;
{
my $foo = 2;
f();
}
sub f {
print "foo is $foo";
}
结果是
foo is 1
因为词法作用域的$foo对函数f也是不可见的,函数f看到的是在最外层的$foo,而local就不同了
$foo = 1;
{
local $foo = 2;
f();
}
sub f {
print "foo is $foo";
}
结果是
foo is 2
这就是动态作用域,对一般的代码来说,local的适用范围并不广,因为本身全局变量就是不提倡使用的,而local只对全局变量起作用,
可能有些时候你需要对内建的全局变量比如 $/, $_ 临时赋值,这时候会用到local。
state -- 词法变量,静态变量
state $foo = 1;
state比较好理解,它就像c中的静态变量一样,只在第一次调用时候创建该变量,之后都是操作的同一个变量
state也是词法变量,所以只在定义该变量的词法作用域中有效,举个例子:
sub count {
state $counter = 0;
$counter++;
return $counter;
}
say count();
say count();
say count();
总结
因为perl的向下兼容性做的非常好,所以perl也抗了很多历史包袱,比如这里提到的不声明直接使用的全局变量。
现在提倡的用法是变量都用my声明,在需要全局包变量的情况下,用our声明,所有代码前都要加上use strict 和use warnings
这两个pragma, 在某些特定的时候会需要local 和 static 。