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

查看所有标签

猜你喜欢:

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

Java Web服务:构建与运行

Java Web服务:构建与运行

Martin Kalin / 任增刚 / 电子工业出版社 / 2009年11月 / 45.00元

本书以示例驱动的方式详尽地介绍了XML Web服务(JAX-WS)和RESTful Web服务(JAX-RS)二者所涵盖的Java相关API。 《Java Web服务:构建和运行》这本书以清晰、务实的方法讲述Web服务相关技术,提供了混合性的架构总结、完全可以运行的代码示例,以及编译、部署和执行应用程序的一些短小精悍的指令。学习本书,读者将掌握如何从零开始编写Web服务或将已有的服务整合到现......一起来看看 《Java Web服务:构建与运行》 这本书的介绍吧!

SHA 加密
SHA 加密

SHA 加密工具

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器