あぁ,なんかやっとスッキリした。見落としていたのはリファレンスカウントだった。すぐに反応がもらえてありがたい限りです>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にアクセスする事が可能になってる。
なんというか、この感覚はクロージャーな感じがする。パッケージにおける変数の扱いについて考えているよりは、スコープとクロージャの扱いについて考えていたのかもしれないな。