内容简介:软件包变量(package variable)一般也被称为全局变量, 使用词法变量(lexical variable)使用关键字符号表类似于一个哈希表, 只是类似, 并不完全相同. 我们可以使用
软件包变量和词法变量
软件包变量(package variable)一般也被称为全局变量, 使用 our
可以声明软件包变量. 软件包变量会被存入 符号表
. 操作符号表和 typeglob 的时候操作的是软件包变量.
词法变量(lexical variable)使用关键字 my
声明, 作用域有限, 且不会被存入 符号表
.
符号表类似于一个哈希表, 只是类似, 并不完全相同. 我们可以使用 keys
关键字来查询其中保存的内容, 比如:
foreach (keys %main::) {
print $_ . "\n";
}
如果有定义 package 的名字, 则可以样查看:
package Foo;
foreach (keys %Foo::) {
print $_ . "\n";
}
其中的 %main::
和 %Foo::
都是符号表.
基于此, 我们使用 keys
可以观察一下使用 our
和 my
声明的变量的不同:
our:
package Foo;
our $bar;
# infact, it will also print "bar" if we declare $bar by using local
# local $bar;
# this will print "bar"
foreach (keys %Foo::) {
print $_ . "\n";
}
my:
package Foo;
my $bar;
# this will print nothing
foreach (keys %Foo::) {
print $_ . "\n";
}
可以很明显的发现, 使用 our
声明的变量会被加入符号表 %Foo::
, 而使用 my
声明的变量则不会. 这是这两种声明方法比较大的区别: 一个声明的是 软件包变量
, 存在于 符号表
中, 另一个声明的是 词法变量
, 不会存在于 符号表
.
our / local / my
其实前面提到了 our
和 local
声明的变量属于一类, 都属于软件包变量, 而 my
声明的属于另一类, 叫做词法变量, 但是他们的关系还是有点复杂, 举些例子来分析和理解.
一. 重复使用 our
声明变量会覆盖前面的变量, 即使是在新的作用域中:
our $foo = 1;
{
our $foo = 2;
}
print $foo; # now $foo is 2
二. 在新的作用域中使用 local
和 my
不会影响原作用域中的变量:
our $foo = 1;
{
local $foo = 2;
}
print $foo; # $foo is still 1
########################
our $foo = 1;
{
my $foo = 2;
}
print $foo; # $foo is still 1
在这里看起来 local
和 my
的表现形式又很类似了, 让人比较迷惑, 其实他们还是有很大区别的, 继续举例子说明.
三. local
会在新作用域中暂时改变符号表中的变量值, 直到作用域退出. 而 my
创建的变量由于不存在于符号表中, 所以不会影响符号表中的同名变量:
our $foo = 1;
sub print_val {
print $foo;
}
{
local $foo = 2;
print_val(); # this will print 2
}
########################
our $foo = 1;
sub print_val {
print $foo;
}
{
my $foo = 2;
print_val(); # this will print 1
}
分析下上面的例子, 在我们调用了一个函数来打印 $foo
的值的时候, 情况又发生了一些变化.
首先, 我们在函数 print_val
中打印的 $foo
肯定是 our $foo
声明出来的. 注意一点, our
和 local
声明的变量都存在于 符号表
中, 可以认为他们就是同一个变量. local
的效果就是在新的作用域中, 暂时把外部的变量的值保存起来, 然后把符号表中该变量的值设置为新的值, 等到作用域退出后, 将保存的值恢复到符号表中. 所以在我们使用 local $foo = 2
将 $foo
重新赋值为 2 以后, print_val
函数打印出来的 $foo
也变成了 2. 因为他们都是 软件包变量
, 而且在新的作用域中, 它已经被暂时替换为了 1.
而第二种写法使用 my
声明变量的时候, 注意它声明出来的是 词法变量
, 并不存在于符号表中, 所以可以认为此时的 $foo
跟外部使用 our $foo
声明的变量 $foo
毫无关系, 根本就是两个变量. 所以我们当把这个词法变量 $oo
声明出来且赋值为 2 的时候, 对 软件包变量
的 $foo
没有任何影响, 自然使用 print_val
函数打印 软件包变量
$oo
的时候依然会打印出最初的值 1 了.
希望好好研究一下此处的区别.
typeglob
与 $foo
是 scalar
, @foo
是 array
, %foo
是 hash
一样, *foo
是 typeglob
. typeglob 也和符号表一样, 类似于一个哈希表但是却不是一个哈希表, 只是行为有些类似.
我们可以从中取值但是不能对其赋值:
$foo = *foo{SCALAR};
@foo = *foo{ARRAY};
%foo = *foo{HASH};
*foo{SCALAR} = 5; # error
*foo{WHATEVER} = 5; # error
而且我们不能通过 keys
来查看 typeglob 中到底有哪些键名:
print keys *foo; # Experimental keys on scalar is now forbidden
实际上, typeglob 中的键名只有如下几种, 是固定不可变的:
| 键值 | 变量写法 | 说明 |
|---|---|---|
| SCALAR | $foo | 标量 |
| ARRAY | @foo | 数组 |
| HASH | %foo | 哈希 |
| CODE | &foo | 函数 |
| IO | - | 文件句柄 |
| GLOB | *foo | typeglob |
| FORMAT | - | 报表的格式 |
| NAME | - | 变量的名字 |
| PACKAGES | - | 包的名字 |
别名 (alias)
我们可以通过把变量的 typeglob 赋值给另一个变量的 typeglob 来创建变量的别名.
创建别名的时候也有两种方式, 一种是创建了完全的别名, 即上一节提到的全部键值对应的部分都成为了别名. 另一种是创建部分别名, 将某个变量的引用赋给另一个变量的 typeglob, 那么只有这个类型的变量创建了别名:
*foo = *bar; # full alias *foo = \$bar; # partial alias
而且除了"完全别名"和"部分别名"这个不同以外, 他们还有一些微妙的区别, 此处需要结合最初讲到的 local
等内容来举例说明:
our $bar = 1;
*foo = *bar;
{
local $bar = 2;
print $foo; # this will print 2
}
########################
our $bar = 1;
*foo = \$bar;
{
local $bar = 2;
print $foo; # this will print 1
}
发现了吗, 在新的作用域中, "完全别名"的写法会受到 local
的影响, 而"部分别名"的写法不会受到 local
的影响.
我们可以通过打印各个变量的内存地址的方式来看看 local
的时候发生了什么, 而使用"完全别名"和"部分别名"两种写法的时候又各自发生了什么.
our $foo;
sub print_val {
print \$foo . "\n";
}
print \$foo . "\n"; # SCALAR(0x3278c0)
{
local $foo;
print \$foo . "\n"; # SCALAR(0x72d430)
print_val(); # SCALAR(0x72d430)
}
结合我们之前提到的知识, 可以发现, 使用 local
的时候, 会在新的作用域中, 创建另一个 软件包变量
, 而且能看见使用函数 print_val
打印出来的变量地址就是我们 local
产生的那个变量的地址, 证明了我们将外部作用域中同名 软件包变量
保存了起来, 然后使用新作用域中 local
产生的变量替换了这个 软件包变量
, 直到作用域退出后恢复以前的那个变量.
继续看:
our $bar;
print \$bar . "\n"; # SCALAR(0x48dfb8)
*foo = *bar;
print \$foo . "\n"; # SCALAR(0x48dfb8)
sub print_val {
print \$foo . "\n";
}
{
local $bar;
print \$bar . "\n"; # SCALAR(0x4fb590)
print \$foo . "\n"; # SCALAR(0x4fb590)
print_val(); # SCALAR(0x4fb590)
}
在这种"完全别名"的写法下, 外部作用域中两个变量 $foo
和 $bar
地址一致, 新作用域中两个变量 $foo
和 $bar
的地址也一致, 说明 foo
一直完全是 bar
的别名, 即使外部的值被缓存, 新作用域创建了新的变量 bar
, foo
依然就是 bar
的别名.
然而:
our $bar;
print \$bar . "\n"; # SCALAR(0x327440)
*foo = \$bar;
print \$foo . "\n"; # SCALAR(0x327440)
sub print_val {
print \$foo . "\n"; # SCALAR(0x327440)
}
{
local $bar;
print \$bar . "\n"; # SCALAR(0x8ad430)
print \$foo . "\n"; # SCALAR(0x327440)
print_val();
}
使用"部分别名"的写法的时候, foo
其实只是外部变量 bar
的别名. 在新的作用域中, 我们虽然使用了 local
创建了一个新的 $bar
, 这会导致新作用域中 $bar
会把外部作用域中同名的 $bar
隐藏起来, 但是 $foo
本身其实只是外部变量的别名, 而且没有同名变量的干扰, 所以不会受到内部 $bar
赋值的影响.
以上所述就是小编给大家介绍的《Perl 符号表和 typeglob》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- macos – dyld:惰性符号绑定失败:未找到符号:_PQsetErrorContextVisibility
- 嵌入式C语言自我修养 09:链接过程中的强符号和弱符号
- Scala中的符号
- “Bug-O” 符号
- 简单理解符号执行技术
- GCC 符号表小结
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Out of their Minds
Dennis Shasha、Cathy Lazere / Springer / 1998-07-02 / USD 16.00
This best-selling book is now available in an inexpensive softcover format. Imagine living during the Renaissance and being able to interview that eras greatest scientists about their inspirations, di......一起来看看 《Out of their Minds》 这本书的介绍吧!