Perl学习笔记(二)--my, our, local 以及 typeglob
Perl中有两种名字空间(namespace):符号表(symbol table)和词法作用域(lexical scopes).在Perl中, 每个包(Package)有一个
符号表, 可以通过Packeagename::获取包中的变量.
比如:
package A;
$test = 1;
可以通过$A::test访问包A的变量test.
符号表是一个Hash表, 该Hash表的名字就是符号表所在包的名字.
比如你可以用下面的语句打印出在main包中的所有符号:
foreach (keys %main::)
{
print $_, "\n";
}
又比如, 如果在程序中声明了:
$test = 1;
可以通过$main::test来访问该变量(假设变量是在main包中).
在同一个符号表中, 可以存在同样名字的标量变量, 数组变量, Hash变量, 函数变量等.Perl中采用*符号来表示一个typeglobs,意思
就是"所有名字为XX的变量",比如:
$type = *test;
此时变量type中存放的就是当前包中所有名字为test的变量,
如果我们调用语句:
print $$type;
则将$type所指向的变量解释为标量, 类似的可以解释为数组, Hash, 函数等等.
但是, 如果你仅仅想让这个typeglobs作用于一个类型的变量, 也可以明确的指出来, 如:
*dick = \$richard;
这样dick变量就只能访问名为richard的标量了.
可以把typeglobs想象为另一个typeglobs的别名, 当写下代码
$type = *test;
的时候, type就是test这个typeglobs的别名了.
Perl中还支持符号引用, 它可以根据一个标量存放的字符串去查找名为该字符串的typeglobs,如:
$test = 1;
$name = "test";
print $$name;
Perl解释器首先去解释变量$name, 发现是一个字符串, 再根据该字符串去解释这个解引用, 也就是说:
$$name -> $test -> 1
在Perl中, 变量的名字是可以和Perl的关键字同名的, 只要在使用的时候有"足够的办法"让Perl解释器去明确辨别出来到底是哪种变量.
比如我写了一个名为for的函数, 一般而言, 在Perl中, 函数调用是可以省略掉前面的&符号的, 但是在这里对这个名为for的函数则不行,
Perl会把它解释为for循环.因此, 要明确的表示是在调用一个叫for的函数, 需要在前面加上&符号.对其它类型的变量, 这个例子同样适用.
在<<Perl语言编程>>(大骆驼)中, 将$&%@这几种分别作用在不同类型变量前面的特殊符号称之为"funny character".
在Perl中, 有三种修饰符是修饰变量的作用域的:our, my, local,它们与前面提到的Perl中的作用域息息相关.
our声明的变量是包全局的, 也就是说, our声明的变量会出现在这个包的符号表中;my声明的变量仅作用于一个块(block), 在Perl中,
一对{}包起来的部分称之为一个块, 比如一个函数是一个块, 一个循环是一个块,等等,my修饰的变量普遍作用于这些地方, 如果没有在哪一个
块中, 那么就仅作用于当前文件, 但是,无论如何, my修饰的变量都不会进入一个包的符号表中, 这也就意味着my声明的变量不能使用typeglobs
进行访问, 如:
my $test = 1;
$type = *test;
print $$type;
这段代码是没有任何效果的, 因为在当前包中找不到名字是test的变量.把上面代码中修饰变量test的my去掉, 再试试就知道区别了.
local是作用于那些在符号表中出现的变量的, 它让对这些变量的修改局限在一个块(block)中, 比如:
our $var;
$var = 1;
{
local $var;
$var = 2;
print $var, "\n";
}
print $var, "\n";
打印的结果是
2
1
local的出现让块中对变量var的修改仅仅局限在这个块中了.
总结:
1. Perl中有两个作用域:符号表和词法作用域, 每个包都有自己的符号表, 这样可以避免名字污染问题的出现, 类似于C++中的
namespace, 全局变量, 以及用our修饰的变量都属于这个作用域, 可以通过:包名称::变量名称来访问一个包中的全局变量.而词法作用域
是指的一个块, 或者一个文件, 凡是由my修饰的变量都是词法作用域中的变量, 它们不会出现在包中的符号表中, 在包之外不可见,
更甚者, 如果一个由my修饰的变量在一个块中, 它就仅仅在这个块中可见了.
2. 如果想让对某个全局变量的修改局部化, 那么就使用local修饰符, 在出了这个块之后, 自动恢复为全局变量原来的数据.
符号表, 可以通过Packeagename::获取包中的变量.
比如:
package A;
$test = 1;
可以通过$A::test访问包A的变量test.
符号表是一个Hash表, 该Hash表的名字就是符号表所在包的名字.
比如你可以用下面的语句打印出在main包中的所有符号:
foreach (keys %main::)
{
print $_, "\n";
}
又比如, 如果在程序中声明了:
$test = 1;
可以通过$main::test来访问该变量(假设变量是在main包中).
在同一个符号表中, 可以存在同样名字的标量变量, 数组变量, Hash变量, 函数变量等.Perl中采用*符号来表示一个typeglobs,意思
就是"所有名字为XX的变量",比如:
$type = *test;
此时变量type中存放的就是当前包中所有名字为test的变量,
如果我们调用语句:
print $$type;
则将$type所指向的变量解释为标量, 类似的可以解释为数组, Hash, 函数等等.
但是, 如果你仅仅想让这个typeglobs作用于一个类型的变量, 也可以明确的指出来, 如:
*dick = \$richard;
这样dick变量就只能访问名为richard的标量了.
可以把typeglobs想象为另一个typeglobs的别名, 当写下代码
$type = *test;
的时候, type就是test这个typeglobs的别名了.
Perl中还支持符号引用, 它可以根据一个标量存放的字符串去查找名为该字符串的typeglobs,如:
$test = 1;
$name = "test";
print $$name;
Perl解释器首先去解释变量$name, 发现是一个字符串, 再根据该字符串去解释这个解引用, 也就是说:
$$name -> $test -> 1
在Perl中, 变量的名字是可以和Perl的关键字同名的, 只要在使用的时候有"足够的办法"让Perl解释器去明确辨别出来到底是哪种变量.
比如我写了一个名为for的函数, 一般而言, 在Perl中, 函数调用是可以省略掉前面的&符号的, 但是在这里对这个名为for的函数则不行,
Perl会把它解释为for循环.因此, 要明确的表示是在调用一个叫for的函数, 需要在前面加上&符号.对其它类型的变量, 这个例子同样适用.
在<<Perl语言编程>>(大骆驼)中, 将$&%@这几种分别作用在不同类型变量前面的特殊符号称之为"funny character".
在Perl中, 有三种修饰符是修饰变量的作用域的:our, my, local,它们与前面提到的Perl中的作用域息息相关.
our声明的变量是包全局的, 也就是说, our声明的变量会出现在这个包的符号表中;my声明的变量仅作用于一个块(block), 在Perl中,
一对{}包起来的部分称之为一个块, 比如一个函数是一个块, 一个循环是一个块,等等,my修饰的变量普遍作用于这些地方, 如果没有在哪一个
块中, 那么就仅作用于当前文件, 但是,无论如何, my修饰的变量都不会进入一个包的符号表中, 这也就意味着my声明的变量不能使用typeglobs
进行访问, 如:
my $test = 1;
$type = *test;
print $$type;
这段代码是没有任何效果的, 因为在当前包中找不到名字是test的变量.把上面代码中修饰变量test的my去掉, 再试试就知道区别了.
local是作用于那些在符号表中出现的变量的, 它让对这些变量的修改局限在一个块(block)中, 比如:
our $var;
$var = 1;
{
local $var;
$var = 2;
print $var, "\n";
}
print $var, "\n";
打印的结果是
2
1
local的出现让块中对变量var的修改仅仅局限在这个块中了.
总结:
1. Perl中有两个作用域:符号表和词法作用域, 每个包都有自己的符号表, 这样可以避免名字污染问题的出现, 类似于C++中的
namespace, 全局变量, 以及用our修饰的变量都属于这个作用域, 可以通过:包名称::变量名称来访问一个包中的全局变量.而词法作用域
是指的一个块, 或者一个文件, 凡是由my修饰的变量都是词法作用域中的变量, 它们不会出现在包中的符号表中, 在包之外不可见,
更甚者, 如果一个由my修饰的变量在一个块中, 它就仅仅在这个块中可见了.
2. 如果想让对某个全局变量的修改局部化, 那么就使用local修饰符, 在出了这个块之后, 自动恢复为全局变量原来的数据.