うーん、さすがにそれはいいすぎでしょうか。
クロージャの概念をクラスとの対比でわかりやすく説明する。 - サンプルコードによる 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 Codepadmy $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 } } ) );
このブログにコメントするにはログインが必要です。
さんログアウト
この記事には許可ユーザしかコメントができません。