内容简介:软件包变量(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 符号表小结
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
URL 编码/解码
URL 编码/解码
RGB CMYK 转换工具
RGB CMYK 互转工具