クロージャが使われているコードを調べてみる。
クロージャについては自分でも理解出来ているとは言い難く、誰かに説明することも出来ないので、クロージャのコードを書いたり紹介してみたいと思います。
Perlでいうと、アルパカ本でも少し紹介されていましたのでそちらを見るとわかりやすいかもしれません。
クロージャとは?
- Wikipediaの概要には下記のように書かれています。
典型的には、クロージャはある関数全体が他の関数(以下、エンクロージャ)の内部で宣言されたときに発生し、内部の関数はエンクロージャのローカル変数(レキシカル変数)を参照する。実行時に外部の関数が実行された際、クロージャが形成される。クロージャは内部の関数のコードとエンクロージャのスコープ内の必要なすべての変数への参照からなる。
クロージャはプログラム内で環境を共有するための仕組みである。レキシカル変数はグローバルな名前空間を占有しないという点でグローバル変数とは異なっている。またオブジェクトのインスタンス変数とは、オブジェクトのインスタンスではなく関数の呼び出しに束縛されているという点で異なる。
クロージャは関数型言語では遅延評価やカプセル化のために、また高階関数の引数として広く用いられる。
- うーん、いまいちイメージをしにくい。。
- クラスやグローバル変数でなく、関数の内部に変数を隠蔽した状態で保存しておけるといった感じでしょうか。
JavaScriptでは
JavaScriptでは、イベント処理などをはじめコールバック関数を渡すときなどによくクロージャが使われていますが、クロージャを使うことで下記のように本当なプライベートなメンバ変数を作ることができたりもします。
- 上記の例では、getName,setName以外からはnameにアクセスすることが出来ず、まさにカプセル化が行われています。
CPANモジュールからクロージャを学ぶ
- やはり実践的なコードからも学びたいということで、「Class::Data::Inheritable 」のコードを取り上げてみたいと思います。
上記の問いかけに対して、@hide_o_55さんがクロージャの教材としてこのモジュールを紹介してくださりました。ありがとうございます!
- というわけでこのモジュール唯一の関数であるmk_classdataのソースを。(日本語での説明を追加しています)
sub mk_classdata { my ($declaredclass, $attribute, $data) = @_; # $declaredclassはクラス名、$attributeはクラスデータ属性名、$dataはクラスデータ。 # インスタンス(リファレンス)から呼ばれたら例外を投げる。クラス関数を定義するため。 if( ref $declaredclass ) { require Carp; Carp::croak("mk_classdata() is a class method, not an object method"); } # クラス関数を作成する。 # クロージャを作成している。対象の変数は$attribute、$declaredclass、$data my $accessor = sub { my $wantclass = ref($_[0]) || $_[0]; # 呼ばれたクラスが作成した時のクラスと異なる場合はクラス関数を作り直す。(オーバーライド) # そのまま値をセットするとスーパークラスのクラスデータを上書きしてしまうため。 return $wantclass->mk_classdata($attribute)->(@_) if @_>1 && $wantclass ne $declaredclass; # セットする値があるときはセットしてその値を返す $data = $_[1] if @_>1; return $data; }; # クラス関数を登録する。 my $alias = "_${attribute}_accessor"; *{$declaredclass.'::'.$attribute} = $accessor; *{$declaredclass.'::'.$alias} = $accessor; }
- 使ってみる
- このモジュールは、クラスデータを作成するためのモジュールで、クラスが継承された時のためにクロージャが使われています。短いソースですが本当に勉強になります。