Perl 符号表和 typeglob

栏目: Perl · 发布时间: 5年前

内容简介:软件包变量(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 可以观察一下使用 ourmy 声明的变量的不同:

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

其实前面提到了 ourlocal 声明的变量属于一类, 都属于软件包变量, 而 my 声明的属于另一类, 叫做词法变量, 但是他们的关系还是有点复杂, 举些例子来分析和理解.

一. 重复使用 our 声明变量会覆盖前面的变量, 即使是在新的作用域中:

our $foo = 1;
{
    our $foo = 2;
}
print $foo; # now $foo is 2

二. 在新的作用域中使用 localmy 不会影响原作用域中的变量:

our $foo = 1;
{
    local $foo = 2;
}
print $foo; # $foo is still 1

########################

our $foo = 1;
{
    my $foo = 2;
}
print $foo; # $foo is still 1

在这里看起来 localmy 的表现形式又很类似了, 让人比较迷惑, 其实他们还是有很大区别的, 继续举例子说明.

三. 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 声明出来的. 注意一点, ourlocal 声明的变量都存在于 符号表 中, 可以认为他们就是同一个变量. local 的效果就是在新的作用域中, 暂时把外部的变量的值保存起来, 然后把符号表中该变量的值设置为新的值, 等到作用域退出后, 将保存的值恢复到符号表中. 所以在我们使用 local $foo = 2$foo 重新赋值为 2 以后, print_val 函数打印出来的 $foo 也变成了 2. 因为他们都是 软件包变量 , 而且在新的作用域中, 它已经被暂时替换为了 1.

而第二种写法使用 my 声明变量的时候, 注意它声明出来的是 词法变量 , 并不存在于符号表中, 所以可以认为此时的 $foo 跟外部使用 our $foo 声明的变量 $foo 毫无关系, 根本就是两个变量. 所以我们当把这个词法变量 $oo 声明出来且赋值为 2 的时候, 对 软件包变量$foo 没有任何影响, 自然使用 print_val 函数打印 软件包变量 $oo 的时候依然会打印出最初的值 1 了.

希望好好研究一下此处的区别.

typeglob

$fooscalar , @fooarray , %foohash 一样, *footypeglob . 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》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

分布式算法导论

分布式算法导论

泰尔 / 霍红卫 / 机械工业出版社 / 2004年09月 / 39.0

分布式算法20多年来一直是倍受关注的主流方向。本书第二版不仅给出了算法的最新进展,还深入探讨了与之相关的理论知识。这本教材适合本科高年级和研究生使用,同时,本书所覆盖的广度和深度也十分适合从事实际工作的工程师和研究人员参考。书中重点讨论了点对点消息传递模型上的算法,也包括计算机通信网络的实现算法。其他重点讨论的内容包括分布式应用的控制算法(如波算法、广播算法、选举算法、终止检测算法、匿名网络的随机......一起来看看 《分布式算法导论》 这本书的介绍吧!

URL 编码/解码
URL 编码/解码

URL 编码/解码

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具