Perl符号引用的问题

起源

今天在qq上被问到了这样的一个问题:

question
1
2
3
4
5
use Data::Dumper;
my %a;
$a{"a"}="a";
$a{"a"}->{"b"}="b";
print Dumper (\%a);

对应的输出是:

output 1
1
2
3
$VAR1 = {
    'a' => 'a'
};

而如果把代码去掉一行:

ommit one line
1
2
3
4
5
6
7
use Data::Dumper;
my %a;

#$a{"a"}="a";

$a{"a"}->{"b"}="b";
print Dumper (\%a);

对应的输出是:

output 2
1
2
3
4
5
$VAR1 = {
    'a' => {
        'b' => 'b'
    }
};

一上来我的第一个感觉就是第一段代码在做和第二段代码相同的事情, 但是很明显不是, 这里的具体情况是怎样的呢?

尝试

把上述的第一种代码简化一下:

ex1
1
perl -MDDP -e 'my %a; $a{"a"} = "a"; p %a; $a{"a"}->{"b"} = "b"; p %a'
ex1 output
1
2
3
4
5
6
{
        a   "a"
}
{
        a   "a"
}

看起来两次赋值没有对my %a产生影响. 那我们来看看$a{"a"}是个what:

ex2
1
perl -MDDP -e 'my %a; $a{"a"} = "a"; p %a; $a{"a"}->{"b"} = "b"; p $a{"a"}'
ex2 output
1
2
3
4
{
        a   "a"
}
"a"

这里我们知道$a{“a”}是一个值为字符串a变量, 即使是对它做了解引用. 好, 既然是在一个有值的变量上做操作那自然就是符号引用了. 我们再来看一下:

ex3
1
perl -MDDP -e 'my %a; $a{"a"} = "a"; p %a; $a{"a"}->{"b"} = "b"; p %{$a{"a"}}'
ex3 output
1
2
3
4
5
6
{
        a   "a"
}
{
        b   "b"
}

好了, 现在我们对$a{"a"}进行hash的解引用(即%{"a"}), 输出了我们预期的结果. 等等, %{$a{"a"}}%a不应该是等价的吗?

我们先来个简单点的情况:

ex4
1
perl -MDDP -e 'my %a; $a{"a"} = "global_a"; p %a; $a{"a"}->{"b"} = "b"; p %global_a'
ex4 output
1
2
3
4
5
6
{
        a   "global_a"
}
{
        b   "b"
}

这个情况比较明显, 我们利用符号引用创建了全局的%global_a变量.

说明

为了可以对上面的各个结果有个明确的我们参考下perlref的说明:

We said that references spring into existence as necessary if they are undefined, but we didn’t say what happens if a value used as a reference is already defined, but isn’t a hard reference. If you use it as a reference, it’ll be treated as a symbolic reference.
That is, the value of the scalar is taken to be the name of a variable, rather than a direct link to a (possibly) anonymous value.

一些例子:

1
2
3
4
5
6
7
8
9
$name = "foo";
$$name = 1;                 # Sets $foo
${$name} = 2;               # Sets $foo
${$name x 2} = 3;           # Sets $foofoo
$name->[0] = 4;             # Sets $foo[0]
@$name = ();                # Clears @foo
&$name();                   # Calls &foo() (as in Perl 4)
$pack = "THAT";
${"${pack}::$name"} = 5;    # Sets $THAT::foo without eval

然后很重要的一句:

Only package variables (globals, even if localized) are visible to symbolic references.
Lexical variables (declared with my()) aren’t in a symbol table, and thus are invisible to this mechanism.

所以我们上面的ex3的解释就是p %a;打印的是局部(lexical)的%a, 而p %{$a{"a"}}打印的是全局(package)的%a.

验证一下:

ex5
1
perl -MDDP -e '{ my %a; $a{"a"} = "a"; p %a; $a{"a"}->{"b"} = "b"; p %a } p %a'
ex5 output
1
2
3
4
5
6
7
8
9
{
        a   "a"
}
{
        a   "a"
}
{
        b   "b"
}

到这里就比较明朗了.

题外话

  1. 忍不住吐嘈下《Programming Perl》, 敢多点和perldoc不一样的地方吗?
  2. 今天的nba打得够激烈的……

最后感谢下飞龙同学

Comments