変数のリファレンスカウント

あぁ,なんかやっとスッキリした。見落としていたのはリファレンスカウントだった。すぐに反応がもらえてありがたい限りです>id:seamlessbiasさん。

これを見るとわかるけど、リファレンスカウント(REFCNT)が2になってる。$nameという名前と、&foo::get_nameという関数から参照されているからね。

変数はスコープから抜けたら消えるわけではない。リファレンスカウントが一つ減らされるだけなんだな。たまたま、スコープから抜けてリファレンスカウントが0になったら変数は片付けられるという事か。

use warnings;
use strict;

{
    package foo;
    my $name = 'foo';
    sub get_name {return $name;}
}

print foo::get_name(), "\n";

これでちゃんと"foo"が表示されるのは、パッケージfooの$nameがまだちゃんと残っているからだ。それはイコール、$nameのリファレンスカウントが0より大きいということ。そして、その残っているリファレンスというのは、foo::get_name()からの参照に他ならない。

id:seamlessbiasさんのやっているように、Devel::Peekで確認してみる。パッケージfooのスコープから出る直前と、foo::getname()を呼び出したときに$nameのリファレンスカウントを表示してみた。

use warnings;
use strict;

{
    package foo;
    my $name = 'hakobe';

    use Devel::Peek;
    sub get_name {
        print "-----";
        print "Inside get_name()\n";
        Dump $name;
    }

    print "-----";
    print "Just begore going out of foo\n";
    Dump $name;
}

foo::get_name();

実行結果:

$ perl ref_count.pl
-----Just begore going out of foo
SV = PV(0x1801460) at 0x1800f34
  REFCNT = 2
  FLAGS = (PADBUSY,PADMY,POK,pPOK)
  PV = 0x401800 "hakobe"\0
  CUR = 6
  LEN = 8
-----Inside get_name()
SV = PV(0x1801460) at 0x1800f34
  REFCNT = 1
  FLAGS = (PADBUSY,PADMY,POK,pPOK)
  PV = 0x401800 "hakobe"\0
  CUR = 6
  LEN = 8

たしかに、foo::get_name()はパッケージfooのスコープで宣言された$nameへの参照をもっていることがわかる。もともと宣言された$nameからの参照はスコープ外にでることで失われる(なので、fooのスコープからでることでリファレンスカウントが一つ減っている)。一方、foo::get_name()からの参照は残っているので変数は残り続けて、foo::get_name()から$nameにアクセスする事が可能になってる。

なんというか、この感覚はクロージャーな感じがする。パッケージにおける変数の扱いについて考えているよりは、スコープとクロージャの扱いについて考えていたのかもしれないな。