Top > MyPage
 

変数のスコープからインサイドアウトクラスまで

スコープからインサイドアウトクラスまで

変数のスコープ

まずは以下の簡単なスクリプトを実行してみましょう。

$var = "サブルーチンの外\n";
print $var;    #「サブルーチンの外」が出力される
&example;
print $var;      #「サブルーチンの外」が出力される

sub example {
  my $var = "サブルーチンの中\n";
  print $var;    #「サブルーチンの中」が出力される
}

すると

サブルーチンの外
サブルーチンの中
サブルーチンの外

と、出力されるはずです。同じ$varという変数に2度代入しています。 mainで

$var = "サブルーチンの外\n";

サブルーチンの中で、

my $var = "サブルーチンの中\n";

です。そして、結果からもわかるように、サブルーチンを実行すると、そのサ ブルーチンの中のprint文で

サブルーチンの中

と表示されますが、サブルーチンの実行後には、サブルーチンの中で $varに「サブルーチンの中」を代入したにもかかわらず、次のprint文 では

サブルーチンの外

が表示されます。これはどうしてそうなるかというと、サブルーチンの中の、

my $var = "サブルーチンの中\n";

myがポイントです。このmyを変数の前に付けることによって、 そのブロック(ここではサブルーチンの中)だけ有効な変数にすることが出来る わけです。Perlではこの変数のことをレキシカル変数と言います。

さて、じゃあ、このmyがついていないとどうなるでしょう。

$var = "サブルーチンの外\n";
print $var;    #「サブルーチンの外」が出力される
&example;
print $var;      #「サブルーチンの外」が出力される

sub example {
  $var = "サブルーチンの中\n";
  print $var;    #「サブルーチンの中」が出力される
}

を実行すると、

サブルーチンの外
サブルーチンの中
サブルーチンの中

と出力されます。つまり、先ほどと違い、サブルーチンの中の$varと外 の$varが、同じ変数となり、サブルーチンでの代入後には、外の $varも「サブルーチンの中」に上書きされてしまったわけです。

次の例は、サブルーチンから別のサブルーチンの呼び出しているプログラムで す。

my $var = "サブルーチンの外\n";
&example;
print $var;      #「example2サブルーチンの外」が出力される

sub example {
  my $var = "exampleサブルーチンの中\n";
  print $var;    #「exampleサブルーチンの中」が出力される
  &example2("example2サブルーチンの中\n");
}

sub example2 {
  $var = shift;
  print $var;    #「example2サブルーチンの外」が出力される
}

を実行すると、

exampleサブルーチンの中
example2サブルーチンの中
example2サブルーチンの中

と出力されます。つまり、サブルーチンの外の$varが、exampleサブルー チンの中から実行されるexample2サブルーチンの中で上書きされているわけで す。

これはちょっと危険です。では・・・・。

スコープをうまく使う

先ほどの2つめや3つめの例の結果は通常プログラマーが望んでいるものではありま せん。

というのも、例えば、500行ぐらいのプログラムを書いていると、相当の数の 変数が登場してきます。或いは自分のプログラムは100行ぐらいの短いものだ としても、その中で

require "hoge.pl";

と、別のプログラムを読み込む場合もあります。だいたい100行だけで終わるよ うなプログラムは大きなことは出来ませんが、確かにその場合はあまりスコー プを気にすることなどないかもしれません。しかしたいていは、そんなことは 滅多にありません。

したがって、変数の「ぶつかり」を避けないと、思わぬバグに悩まされること になります。その1つの方法として、次のプラグマがあります。

use strict;

このプラグマ(perlをインストールしたときに、すでにインストールされてい るモジュール群で、「コンパイルに影響を与え、またはコンパイル時のみ作用 するモジュールのこと」を「プラグマ」と言い、それ以外を「標準モジュール」 という)を利用すると、myなどを付けることを強制されます。つまり

use strict;
$var = "サブルーチンの外\n";
print $var;    #「サブルーチンの外」が出力される
&example;
print $var;      #「サブルーチンの外」が出力される

sub example {
  $var = "サブルーチンの中\n";
  print $var;    #「サブルーチンの中」が出力される
}

を実行すると、始めて出てくる変数varmyを付けないと、以下 のようにPerlに怒られます。

Global symbol "$var" requires explicit package name at testScope3.pl line \
    2.
Global symbol "$var" requires explicit package name at testScope3.pl line \
    3.
Global symbol "$var" requires explicit package name at testScope3.pl line \
    5.
Global symbol "$var" requires explicit package name at testScope3.pl line \
    8.
Global symbol "$var" requires explicit package name at testScope3.pl line \
    9.

つまり、実行すら出来なくなるので、強制されるわけです。次のように すれば、実行できます。

use strict;
my $var = "サブルーチンの外\n";
print $var;    #「サブルーチンの外」が出力される
&example;
print $var;      #「サブルーチンの外」が出力される

sub example {
  my $var = "サブルーチンの中\n";
  print $var;    #「サブルーチンの中」が出力される
}

local

local を使って定義された変数は、ダイナミックスコープ変数と呼びます。そして、local を使って変数を宣言することをダイナミックスコープ宣言と呼びます。

特に僕のように古くからPerlをやっている人にとってはなかなか、mylocalの区別が付かないものですが、次のスクリプトで理解すると、わか りやすいかもしれません。

our $var = "サブルーチンの外\n";
&example;
print $var;    #「サブルーチンの外」が出力される

sub example {
  local $var = "サブルーチンの中\n";
  print $var;    #「サブルーチンの中」が出力される
  &example2;
}

sub example2 {
  print $var;    #「サブルーチンの中」が出力される
}

上記を実行すると、

サブルーチンの中
サブルーチンの中
サブルーチンの外

となります。localという部分がmyにすると

our $var = "サブルーチンの外\n";
&example;
print $var;    #「サブルーチンの外」が出力される

sub example {
  my $var = "サブルーチンの中\n";
  print $var;    #「サブルーチンの中」が出力される
  &example2;
}

sub example2 {
  print $var;    #「サブルーチンの中」が出力される
}

サブルーチンの中
サブルーチンの外
サブルーチンの外

となり、example2サブルーチンの出力が違います。最初の例でこれは localが、exampleサブルーチンの中で

sub example {
  local $var = "サブルーチンの中\n";
  #################################
  print $var;    #「サブルーチンの中」が出力される
  &example2;
}

で、宣言されているわけですが、ここでもともとの$varが、メモリーの どこかにスタックされ(つまり保存される---多分First In Last Outという形 式で)、新たにそのスコープ(現実にはexampleサブルーチンの間だけ)内で $varが作成されるわけです。

つまりexampleサブルーチンの中ではもともとの$varは待避していて、新 たな$varが作成され、そのサブルーチンが終わるときに、もともとの $varが復帰することになります。

このように動的に$varを待避したり、新たに作成したり、元に戻したり するので、ダイナミックスコープ変数と呼ばれているのだと思います。

Chip Salzenbergという人が、「もしタイムマシーンに乗って1986年に戻ること が出来たら、Larryに会って、localの代わりに"save"という名前を付け るように忠告するだろう」という話が「初めてのPerl」で紹介されています(そ の本の作者は註で、私たちならLarryに「Yahooの株を買え」とアドバイスする でしょう、とJokeを言っています)。

一方で、myは、どうかというと(多少推測も含めて言うと)、あるスコー プで、my宣言をした変数があると、そのたびに新たにメモリー上にその 変数を保存する場所を確保します。つまり、同じ変数名でも、別々に管理され るわけです。この点がlocalとは違うところです。

そして、そのスコープを抜けて、かつどこからでも参照されなくなると、 その変数は破棄されます(ガーベッジコレクションの対象になる)。

このどこからでも参照されなくなるは後に出てくる「クロージャー」 という考え方の時に重要になるので、覚えておいてください。

さて、したがって、基本的にはlocal使わないというのが正し いのだと思います。

しかし、どうしても必要な場合があります。例えば、Test::DatabaseRowとい うDBのテストのモジュールでは

local $Test::DatabaseRow::dbh = $dbh;

という形で、使用するDBのURLなどを設定します。このTest::DatabaseRowの中 身をみてみると、

$args{dbh} ||= $Test::DatabaseRow::dbh

row_okメソッドに上記のようなコードがあり、変数としては何も宣言してない のですが、逆にそれ故、グローバル変数(パッケージ変数)として扱われている ようです。したがって、直接

$Test::DatabaseRow::dbh = $dbh;

というような代入が出来るわけですが、ここでlocalというのを付ける 理由は(このテスト内ではあまり意味がありませんが)、「自分が担当していると ころではこの変数$dbhとして利用するけど、他は自分で決めてね」とい う意思表示する、ということです。

「Perlベストプラクティス」でもYAMLのインデント量に関するパッケージ変数 を変更するという際に、「自分の好みをプログラム全体に押しつけるのではな く、自分の好みに合わせるのは自分が担当する部分だけにする」と述べ、パッ ケージ変数を変更するにはlocalを付けるように促しています。

クラス

以上、スコープについて色々と見てきたのですが、ここでちょっとクラスにつ いて考えてみようと思います。

オブジェクト指向ということ自体の有用性は、ここでは敢えて述べません。一 長一短もあるでしょうし、コーディングの姿勢自体も違ってくる問題ではある のですが、このスコープということに着目して、考えてみようと思います。

クラスの一般的な書き方は

package Hoge;

#クラスメソッドコンストラクタ
sub new{
    my $class = shift;
    # 無名ハッシュのリファレンスを作成
    my $self = {};
    # bless したオブジェクトリファレンスを返す
    return bless $self, $class;
}

sub someMethod{
    my $this = shift;
    .......
}

....
1;

という形を取ります。このコンストラクターは(クラスのインスタンスをつくるものという程度で理解しておいてください)、特にnewにする必要がないの ですが、Javaなどとの互換性ということで多くはnewを使うことが多いようです。

さて、上記を簡単に説明をすると、別のプログラムの中から

use Hoge;
my $c = Hoge->new();

を行うとどうなるかというと、Hogeクラスのnewというメソッドを実行すると 言うことですが、その際、見かけでは何も引数を与えていませんが、実際には 1番目の引数として、newメソッドに

my $c = Hoge->new();
        ^^^^

というHogeというクラス名が渡ります。つまり、newメソッドの中で

sub new{
    my $class = shift;
    ^^^^^^^^^^^^^^^^^^
    my $self = {};
    return bless $self, $class;
}

で、クラス名を第1引数として受け取ることになります。そして、

my $self = {};

で、空の無名ハッシュを$selfを作成し、最後に

return bless $self, $class;

で、blessしています。ここのところを少々分けて書けば、

bless $self, $class;
return $self;

となります(blessが返値として、$selfを返すので1行でも書ける)。ではその blessはいったい何をするかというと、この場合だと

$selfという無名ハッシュのリファレンスはHogeというクラス内のオブジェクトにしますよとperlにお知らせするわけです。

もっと大胆に、実用面に着目して換言すれば、

この無名ハッシュに値を入れられるのはもとより、そのハッシュを通じて、Hogeクラスのメソッドも実行できますよ

というような意味合いになります。

さて、とっても簡単なクラスを考えてみましょう。

package Hoge;

sub new{
    my $class = shift;
    my $self = {default=>1};
    return bless $self, $class;
}

sub printDefault{
    my $this = shift;
    print $this->{default},"\n";
}

package main;

my $instance = Hoge->new();

$instance->printDefault();

これを実行すると、

1

と出力されます。

Perlでは1つのファイルの中にパッケージをいくつも書けます。上記ではHogeというパッケージ(Hogeクラスの実装)とmainという特殊なパッケージを書いていますが、このmainというパッケージは実行するためのパッケージなっていますので、上記を(ファイル名をHoge.pmとすると)

perl Hoge.pm

で、実行できるわけです。さて、もう少し詳しく見てみると、

sub new{
    my $class = shift;
    my $self = {default=>1};
    ^^^^^^^^^^^^^^^^^^^^^^^
    return bless $self, $class;
}

で、最初の例では空の無名ハッシュのリファレンスを作成していましたが、今回はdefaultがキーで値が1の値を1つだけ持つハッシュをnewで作成しています。そして、その無名ハッシュを返しているので、

my $instance = Hoge->new();

で、$instanceはその無名ハッシュのリファレンスがセットされます。ただし、前に述べましたように、ただの無名ハッシュのリファレンスではありません。Hogeクラスと関連づけられているので、

$instance->printDefault();

というように、その無名ハッシュのリファレンスを通じて、Hogeクラスのメソッドを実行できるようになります。

また、newの時と同様に(意味合いは微妙に違いますが)

$instance->printDefault();

とやると、このprintDefault()メソッドには、$instance自体が第1引数として渡されます。つまり

sub printDefault{
    my $this = shift;
    print $this->{default},"\n";
}

$thisには、先ほどの無名ハッシュが入っており、したがって

print $this->{default},"\n";

で、その無名ハッシュのdefaultがキーになっている値をprintしていることになります。ここで仮に

Hoge->printDefault();

とやっても、$this->{default}には値が入っていないので、改行だけが表示されることになります。このようなメソッドの実行の仕方は、クラスメソッドとして実行する感じですが、通常は

$instance->printDefault();

というインスタンスメソッドとして使う方法が一般的でしょう。ただ、クラス内部の状態など関係なく、単に、ある値を与えたら、計算等をして値を返すだけのクラスであるならば、クラスメソッドもあり得るかもしれません。

Javaではクラスメソッドとインスタンスメソッドでは定義自体が違うので、インスタンスメソッドをクラスメソッドとして実行することがもともと不可能ですので、クラスの制作者の意図したもの以外の使い方ができないので、ほとんど問題はありませんが、Perlの場合は上記のような形では、プログラム的に強制が出来ません($thisがリファレンスじゃなかったら、とうようなコードを入れることは可能かもしれませんが)。

なので、通常はインスタンスメソッドとしてメソッドは実行するものだというように決めておくと、混乱しないように思います。

さて、ここでスコープという観点で1つ問題が浮かび上がってきます。

package Hoge;

sub new{
    my $class = shift;
    my $self = {default=>1};
    return bless $self, $class;
}

sub printDefault{
    my $this = shift;
    print $this->{default},"\n";
}

package main;
my $instance = Hoge->new();
$instance->printDefault();

$instance->{default} = 100;
$instance->printDefault();

では、最後の2行が追加されています。つまり

$instance->{default} = 100;

で、無名ハッシュのリファレンスのキーがdefaultの値を変更しています。これがこのクラスの制作者の想定しているものであるならば問題ありませんが、もし勝手に変更されるとまずいものだとすると問題が発生します。つまりこれを防ぐことが出来ないわけです。

また、別の視点で言うならば、

$instance->{defalt} = 100;
            ^^^^^^

のようにタイポしてしまった場合でも、何の警告も出来ません。単に、無名ハッシュにキーがdefaultとdefaltのデータが出来ただけです。これを避ける方法いくつかPerlでも生み出されてきているようですが(その中の疑似ハッシュというのは、既にdeprecatedになっており、もうすぐ完全に廃止されるそうなので避けるべきでしょう)、その中で「Perlベストプラクティス」や「オブジェクト指向Perl」などで推奨されているInsideOutオブジェクトというものがありますが、もう少し寄り道をします。

パッケージのスコープ

この題名は実は存在しません。つまり、パッケージにはスコープというものがなく、実際にはファイル毎にスコープがあるようです。すなわち

package Hoge;

my $default = "sakai";

sub new{
    my $class = shift;
    my $self = {default=>1};
    return bless $self, $class;
}

sub printDefault{
    my $this = shift;
    print $this->{default},"\n";
}

package Geho;

my $def = "chikkun";

package main;
my $instance = Hoge->new();
$instance->printDefault();

$instance->{default} = 100;
$instance->printDefault();

print $default , "\n";
print $def , "\n";

を一つのファイルに書いておき(例えば、Hoge.pm)、それを実行すると

1
100
sakai
chikkun

と出力されます。また、別のファイルに次のようなコードを書いておき、

package Huga;
use Hoge;

my $huga = "chikkun";

package main;
my $instance = Hoge->new();
$instance->printDefault();

$instance->{default} = 100;
$instance->printDefault();

print $default , "\n";
print $def , "\n";
print $huga , "\n";

これを実行すると

1
100
sakai
chikkun
1
100


chikkun

と出力されて、Hoge.pmの$defaultや$defには値が入っておらず、改行だけが出力されていることがわかると思います。

なので僕の個人的感想ですが、複数のパッケージを1つのファイルに書き、そのファイル内がスコープの(ある意味でグローバル変数的使っている?)変数を使うことはあまりお薦めできません。というのも、そのファイルの中身が段々膨れあがってきて、ファイルを分割しようとなったとき、変数のスコープが変わってしまい、色々と変更せざるを得なくなります。

また、

package Hoge;

our $default = "sakai";

sub new{
    my $class = shift;
    my $self = {default=>1};
    return bless $self, $class;
}

sub printDefault{
    my $this = shift;
    print $this->{default}," at Hoge//\n";
    return;
}

package Geho;

our $def = "chikkun";

を用意し

package Huga;
use Hoge;
my $huga = "chikkun";

package main;
my $instance = Hoge->new();
$instance->printDefault();

$instance->{default} = 100;
$instance->printDefault();

#############################
print $Hoge::default, "//\n";
print $Geho::def,     "//\n";
Hoge::printDefault();
Hoge->{default} , ">>>>>>>>>>>>\n";
#############################
print $huga , "\n";

を実行すると、

1 at Hoge//
100 at Hoge//
sakai//
chikkun//
 at Hoge//
chikkun

が出力されます。

Hoge::printDefault();

によって、

at Hoge//

が出力されたり、

print $Hoge::default, "//\n";

によって、

sakai//

が出力されたりしています。もちろん、

package Hoge;

my $default = "sakai";
#####################
sub new{
    my $class = shift;
    my $self = {default=>1};
    return bless $self, $class;
}

sub printDefault{
    my $this = shift;
    print $this->{default}," at Hoge//\n";
    return;
}

package Geho;

my $def = "chikkun";
##################

のように、ourmyにかえると、

print $Hoge::default, "//\n";

は出力されません。しかし、

Hoge::printDefault();

は、実行できていまいます。また、

$instance->{default} = 100;

で、値を変えられるわけですが、それを隠す必要がないとか、もともと現在のモジュールが誰も使わないモジュールだとか、変更されても何の影響もないとか、というような場合にはカプセル化(つまり、さわって欲しくない変数やメソッドは、そのクラスを使う側に公開しない---すなわち、全くアクセスできないようにする)は必要ないかもしれません。

しかし、ちょっと凝ったクラスなどを作成してくると、ある変数の値によってメソッドの挙動を変えるなどの要求などが出てくるだろうし、それが使う側から変更されると混乱してしまうような場合もあると推測できます。

しかも、それがはじめからわかっているのではなく、クラスを書いているうちに色々と決まってくる場合もあり、Javaなどでは

変数は基本的にprivate(外からは見えない)ようにし、アクセスする必要があれば、それをpublic(外から見える)なメソッドでその要望に応えるというやり方が一般的です。

可能ならPerlでもそうしたい、というような要望から先ほど触れたInsideOutオブジェクトという方法が推奨されているわけです。

ただし、もう少し寄り道をしないとその実現方法がわからないので、次に、クロージャーに触れます。

クロージャー(closure)

「オブジェクト指向Perl」によれば、Perlの場合、「クロージャーは、(サブルーチンそれ自身の)外部で宣言されたレキシカル変数を参照するサブルーチンを表すに過ぎない」とあります。

つまり

my $name = "chikkun";

sub printName {
    print $name, "\n";
}

&printName;

の、printNameというサブルーチンはクロージャーと言うことになります。

しかし、通常はもう少し込み入った意味で使うことが多いようです。

{
    my $name = "chikkun";
}

print $name,"//\n";

sub printName {
    print $name, "///\n";
}

&printName;

を実行すると

//
///

と出力されて、chikkunという文字列は入っていません。当然といえば当然です。

{
    my $name = "chikkun";
}

において、$nameの宣言が、ブロック内({}内)で宣言されているので、ブロックから出るとスコープから外れるので、ブロック外からはアクセスできないからです。

さて、ここからがポイントです。じゃあ、このブロック内でクロージャーを(ここでは単純にサブルーチンという程度の意味)定義するとどうなるでしょう。

{
    my $name = "chikkun";

    sub printName {
        print $name, "///\n";
    }

}

print $name, "//\n";

&printName;

$name = "sakai////\n";

&printName;

これを実行すると、

//
chikkun///
chikkun///

と出力されます。つまり、

print $name, "//\n";

では$nameにはアクセスできませんが、

&printName;

では、アクセスできることを意味します(サブルーチンのスコープという考え方はない?)。

さらに、

$name = "sakai////\n";

とやっても、ブロック内の$nameは何の影響もない、という結果を表しています。

言い換えれば、ブロックを抜けても、$numという変数は破棄されずに残っており、しかし一方では、その値を変更する術がないということを意味しています。これは前の方で触れた、どこからでも参照されなくなるとレキシカル変数は破棄されるが、どこからか参照が可能な場合は破棄されないという性質によるものです。

また、次のようなコードを実行すると

{
    my $num = 0;

    sub plusOne {
       $num ++;
       print $num, "///\n";
    }

    sub plusTwo {
       $num += 2;
       print $num, "///\n";
    }

}

&plusOne;
&plusTwo;




1///
3///

が出力されます。これは、同じブロック内では変数が共有できることを意味しています。

さあ、最後。インサイドアウトオブジェクトの登場です。

インサイドアウトオブジェクト

上記のクロージャーの特性を利用して、perlのクラスを作る際に、今までのオブジェクト指向の標準規則をことごとく逆らう方法が考案されました。このことごとく逆らう方法から「インサイドアウトオブジェクト=ひっくり返しオブジェクト」という名前が付いているそうです。

とにかく、例を見てみましょう。

package Address;
use strict;
use warnings;
use Carp;
use Class::Std::Utils;
{
    my %address;

    sub new {
        my ( $class, $add ) = @_;

        my $new_object = bless \do { my $anon_scalar }, $class;
        if($add->{address}){
           $address{ident $new_object} = $add->{address};
        }

        return $new_object;
    }

    #
    sub get_address {
        my ($self) = @_;

        return $address{ ident $self};
    }

    sub set_name {
        my ($self, $name) = @_;
        $address{ident $self}->{name} = $name;
    }
}

package main;

my $address = {add=>'Tokyo', tel=>'090-1535-3319',name=>'sakai'};

my $instance = Address->new({address => $address});

my $a = $instance->get_address;

foreach my $key (keys %$a){
    print $key ,":",$$a{$key} , "\n";
}

$instance->set_name("tomoko");

$a = $instance->get_address;

foreach my $key (keys %$a){
    print $key ,":",$$a{$key} , "\n";
}

これは、いつものハッシュベースのクラスではなく、スカラーベースのクラスということになるでしょう。

my $new_object = bless \do { my $anon_scalar }, $class;

まず上記の部分がポイントです。これを理解するには無名ハッシュや無名配列というものを定義する方法はperlにありますが、無名スカラーを定義する方法がないために上記のような方法がとられていることを理解する必要があります。ちなみに、

my $anonHash = {name=>'sakai',age=>36};

は無名ハッシュですし、

my $anonArray = [1,'sakai',$anonHash];

は無名配列になることはおわかりでしょう。しかし、このような名前のないスカラー変数はハッシュなどのような方法はありません。

それで、上記のような方法がとられているわけですが、これはdoブロックもブロックですので、その中で定義された変数はそのブロック内だけで生きていて、抜け出たとたんに消えてしまうわけですが、上記のクロージャーでも述べたように、その変数への参照が存在する限り消えてはなくならないことを利用しています(名前は消えてしまうそうです)。

\do { my $anon_scalar }

で、起こっていることは、doブロックの最後がmy $anon_scalarなので、そのdoブロック自体がその変数を評価し返します。そして、\を付けることにより、その参照を表すことになり、結果として

my $new_object = bless \do { my $anon_scalar }, $class;

$classに入っているクラス名に、$anon_scaarのリファレンスをblessし、そのリファレンスを$new_objectに代入したことになります。

さらに、クラスの定義自体を{}ブロックで囲んでいるので、直接クラス内の%addressにはアクセスできないので、上記のようなset_nameのようなメソッドを通してしかアクセスできないようにできるというわけです。

ここで注意すべきは%addressはハッシュですが、そのキーは常に、そのクラスにユニークなident $selfで生成したものなので、通常のハッシュとしての使い方は出来ません。つまり1つだけスカラーを代入できるだけです。ただし、これをハッシュのリファレンスにすれば良いわけで、けっして単なる文字列や数字しか入れられないわけじゃありませんから、心配はいりません(後で出てくる「Class::Std」を使って、自動でゲッターやセッターを作る方法では、ハッシュのリファレンスの中身までは面倒を見てくれません、残念ながら---知らないだけかもしれませんが)。

また、デストラクターを定義する必要があるようです。

というのも(ちょっと自信はありませんが)、通常のハッシュベースのクラスの場合、blessされたハッシュ自体が参照されなくなった時点で、クラス内の変数への参照がなくなるので、基本的にはそのハッシュ自体がガーベッジコレクションへと送られ、消滅します。

しかし、インサイドアウトオブジェクトでは、blessしたスカラーへの参照がなくなったとしても、確かに上記の$new_objectは消滅するかもしれませんが、%addressへの参照自体はset_nameからのものが残っており、この%addressはガーベッジコレクションへとは送られないそうです。つまり、下手をするとメモリーリークが起こり、時には正体不明な「メモリー不足によるセグメンテーションフォールト」が起こったりする可能性があります。

したがって、しっかり上記の例でいえば、デストラクターで%addressを削除する必要があります。

package Address;
use strict;
use warnings;
use Carp;
use Class::Std::Utils;
{
    my %address;

    sub new {
        my ( $class, $add ) = @_;
        my $new_object = bless \do { my $anon_scalar }, $class;
        if($add->{address}){
           $address{ident $new_object} = $add->{address};
        }
        return $new_object;
    }

    #
    sub get_address {
        my ($self) = @_;

        return $address{ ident $self};
    }

    sub set_name {
        my ($self, $name) = @_;
      $address{ident $self}->{name} = $name;
    }
    #########################  
    sub DESTROY {
      my ($self) = @_;
      delete $address{ ident $self};

      return;
    }
    #########################  
}

package main;

my $address = {add=>'Tokyo', tel=>'090-1535-3319',name=>'sakai'};

my $instance = Address->new({address => $address});

my $a = $instance->get_address;

foreach my $key (keys %$a){
    print $key ,":",$$a{$key} , "\n";
}

$instance->set_name("tomoko");

$a = $instance->get_address;

foreach my $key (keys %$a){
    print $key ,":",$$a{$key} , "\n";
}

インサイドアウトオブジェクトを簡単に

CPANは偉大で、上記のようなインサイドアウトオブジェクトですが、これをもっと簡単に構築するモジュールがあります。これを使うと、わずかに上記のコードが以下のようになります。

package Address;
use strict;
use warnings;
use Carp;
use Class::Std;
{
    my %address     :ATTR( :name<address>   :init_arg<address>);

    sub set_name {
        my ($self, $name) = @_;
        $address{ident $self}->{name} = $name;
    }
}

package main;

my $address = {add=>'Tokyo', tel=>'090-1535-3319',name=>'sakai'};

my $instance = Address->new({address => $address});

my $a = $instance->get_address;

foreach my $key (keys %$a){
    print $key ,":",$$a{$key} , "\n";
}

$instance->set_name("tomoko");

$a = $instance->get_address;

foreach my $key (keys %$a){
    print $key ,":",$$a{$key} , "\n";
}

さすがに、set_nameメソッドまでは、自動では作成してくれないのですが、上記では

my %address     :ATTR( :name<address>   :init_arg<address>);

によって、:ATTRという属性(trait)を付けることによって、自動で作成制されたDESTROYで、それらの変数をガーベッジコレクションに送ってくれます。また、:name<address>によって、それら変数へのセッター、ゲッターも自動作成してくれます(get<address>set<address> )で個別に作成することも可能です)。

また、お気づきでしょうが、

my $instance = Address->new({address => $address});

で、初期化していますが、これも:init_arg<address>で実現しています。

これは、使わないと損かも。

[2007-02-03 10:34]