何がしたかったか
use
しているモジュール側にクロージャがある場合、何度呼び出しても値を保持しておいて欲しいなーって思いました。
元のコード
use strict;
use warnings;
use CloserModule;
my $closer_module = CloserModule->new();
print $closer_module->get_count(), ', ';
print $closer_module->get_count();
exit;
package CloserModule;
use strict;
use warnings;
sub new {
my $pkg = shift;
bless {}, $pkg;
}
sub get_count{
my $self = shift;
my $closer = sub {
my $count = 0;
return sub {
$count++;
return $count;
};
}->();
return $closer->();
}
1;
1, 1
closer.pl側でget_countを呼び出すたびに$closer
という変数が初期化されているので、毎回$count
が0になってしまいます。
できれば$count
を保持し続けてほしいな、と思ったのでした。
修正したコード
偉大な先人たちのモジュールを見ていると、変数がサブルーチンの外で宣言・初期化されていることがよくあったので、真似してみました。
use strict;
use warnings;
use CloserModule;
my $closer_module = CloserModule->new();
print $closer_module->get_count(), ', ';
print $closer_module->get_count();
exit;
package CloserModule;
use strict;
use warnings;
sub new {
my $pkg = shift;
bless {}, $pkg;
}
my $closer = sub {
my $count = 0;
return sub {
$count++;
return $count;
};
}->();
sub get_count{
my $self = shift;
return $closer->();
}
1;
1, 2
変数$closer
をサブルーチンの外で宣言・初期化しているので、何度get_count
を呼んでも$count
の値は保持されたまま($count
が再度宣言・初期化されないまま)になります。
ついでに
use strict;
use warnings;
use CloserModule;
my $closer_module = CloserModule->new();
print $closer_module->get_count(), ', ';
print $closer_module->get_count(), ', ';
my $closer_module2 = CloserModule->new();
print $closer_module2->get_count(), ', ';
print $closer_module2->get_count();
exit;
とした場合。
1, 2, 3, 4
となりました。
つまり、サブルーチン外の変数はnew
した際に初期化されるのではなくて、use
した際に初期化されて、モジュール側に値が残り続けるということなのですね。
当然といえば当然ですが。。。
newした際には$countを0からスタートさせたい
package CloserModule;
use strict;
use warnings;
my $closer;
sub new {
my $pkg = shift;
$closer = sub {
my $count = 0;
return sub {
$count++;
return $count;
};
}->();
bless {}, $pkg;
}
sub get_count{
my $self = shift;
return $closer->();
}
1;
のように、$closer
はサブルーチンの外で宣言をして、new
の中で中身を代入してあげれば、new
した際に$count
が(というか、$closer
自体が)初期化されます。
これで、結果は下記の通りになりました。
1, 2, 1, 2
しかし、
use strict;
use warnings;
use CloserModule;
my $closer_module = CloserModule->new();
print $closer_module->get_count(), ', ';
print $closer_module->get_count(), ', ';
print $closer_module->get_count(), ', ';
my $closer_module2 = CloserModule->new();
print $closer_module2->get_count(), ', ';
print $closer_module2->get_count(), ', ';
print $closer_module->get_count(); #(1)
exit;
とした場合に、(1)の部分では4が返るのを期待していましたが、実際には、、、
1, 2, 3, 1, 2, 3
となりました。
サブルーチン外の変数は影響範囲が大きいですね。
前項の(1)で4が返るようにする
思いついたのは、おとなしくbless
すれば? という事くらいしかありませんでした。
package CloserModule;
use strict;
use warnings;
sub new {
my $pkg = shift;
bless {
closer => sub {
my $count = 0;
return sub {
$count++;
return $count;
};
}->(),
}, $pkg;
}
sub get_count{
my $self = shift;
#return $closer->();
return $self->{closer}->();
}
1;
でもこれだったら普通に
package CloserModule;
use strict;
use warnings;
sub new {
my $pkg = shift;
bless {
count => 0,
}, $pkg;
}
sub get_count{
my $self = shift;
return ++$self->{count};
}
1;
で良かったりします。
なんか他に方法ないのかなー?
もちろん、bless
でもいいんですけどね。
何はともあれ、想定とは違う部分でいろいろ勉強になりました。