うーん、さすがにそれはいいすぎでしょうか。
クロージャの概念をクラスとの対比でわかりやすく説明する。 - サンプルコードによる Perl 入門ここで気づいてほしいことは、クラスとクロージャは、実は同じものだということです。
たしかにオブジェクトの定義から行くと、
404 Blog Not Found:オブジェクトは難しくない。難しいのはクラスそれで、オブジェクトとは何か、といえば、「自分が何が出来るのか[コード]を知っているデータ」ということになる。
であり、クロージャー(closure)は「自分が何を持っているか[データ]を知っているコード」なので、等価ではある。実際、
package Class;
sub new {
my $pkg = shift;
bless {@_}, $pkg
}
sub property {
my $self = shift;
return $self->{property} unless @_;
$self->{property} = shift;
}
は、以下のようにしてclosureにやらせることも出来る。
package Closure;
sub new{
my $instance = { @_ };
return sub{
my $property = shift;
return $instance->{$property} unless @_;
$instance->{$property} = shift;
}
}
実際にその様子を確認してみよう。
Run via Codepad
my $o = Class->new;
$o->property('dan');
warn $o->property;
my $c = Closure::new;
$c->(property => 'kogai');
warn $c->('property');
Class->newをClosure::newに、$o->property()を$c->('property')に書き直せば、見事に一対一対応する。
しかし、実際の運用では、こうなる。
- new
Rate Closure Class Closure 167276/s -- -68% Class 529717/s 217% --- store
Rate Closure Class Closure 967909/s -- -1% Class 977938/s 1% --- fetch
Rate Closure Class Closure 945327/s -- -0% Class 948318/s 0% --
一端生成してしまえば通常、すなわちクラスベースのオブジェクトと遜色のないクロージャーだが、その生成コストはperlにおいては比較的高い。すなわちより多くの時間とメモリーを消費する。(ほぼ)完全なカプセル化という利点がありつつも、Perlにおいてオブジェクトの実装にクロージャーが用いられていない理由の一つがこれだ。ちなみにLispではクロージャーの生成コストは「ただのS式」の生成コストとほぼ同じということもあってか、他がOOでやることをクロージャーでやるのが一般的だったりする。
ちなみに、(ほぼ)完全なカプセル化を実装するもう一つの方法として、Inside-Out Objectという手法が存在している。本blogでも
で紹介しているのであわせて参照のこと。こちらの方が速度面では有利である、ただし通常のハッシュリファレンスによる実装よりは低速であり、そして簡単にシリアライズできないという欠点がある(カプセル化という点ではこれはむしろ利点ではあるが。Mooseで生成したオブジェクトも、「見た目」は単なるblessされたハッシュリファレンスだ。
ちなみに、Perlではリファレンスはblessさえすればオブジェクトとなるので、「クロージャーかつオブジェクト」というものが存在する。拙作Scalar::Lazyは、これを活用している。
というわけで結論。
- クロージャーにはクラスと同じことが出来る。
- ただし、Perlにおいてはそのコストは比較的割高である。
ところで、
クロージャの概念をクラスとの対比でわかりやすく説明する。 - サンプルコードによる Perl 入門クロージャは、継承できない。クラスのように継承をすることはできません。
ところが、できるんだなこれが。宿題にしときます。
Dan the Perl Monger
#!perl
use strict;
use warnings;
{
package Class;
sub new {
my $pkg = shift;
bless {@_}, $pkg
}
sub property {
my $self = shift;
return $self->{property} unless @_;
$self->{property} = shift;
}
}
{
package Closure;
sub new{
my $instance = { @_ };
return sub{
my $property = shift;
return $instance->{$property} unless @_;
$instance->{$property} = shift;
}
}
}
{
my $o = Class->new;
$o->property('dan');
warn $o->property;
my $c = Closure::new;
$c->(property => 'kogai');
warn $c->('property');
}
use Benchmark qw/timethese cmpthese/;
my ($o, $c);
cmpthese(
timethese(
0,
{
Class => sub { $o = Class->new },
Closure => sub { $c = Closure::new }
}
)
);
cmpthese(
timethese(
0,
{
Class => sub { $o->property(1) },
Closure => sub { $c->( property => 1 ) }
}
)
);
cmpthese(
timethese(
0,
{
Class => sub { $o->property == 1 or die },
Closure => sub { $c->('property') == 1 or die }
}
)
);

このブログにコメントするにはログインが必要です。
さんログアウト
この記事には許可ユーザしかコメントができません。