お久しぶりです、コウスケです。
先日のアジアカップ日本代表
戦のあまりに早い敗退、本当に残念でした・・![]()
とはいえ仕方のないこと、次に向けて切り替えていきたいと思います![]()
さて、前回は、関数型プログラミングの基本機能としてmap/fiter/reduceを紹介しました。
それらは関自体数を関数の引数として渡せることを利用した機能でしたが、
今回は関数自体を戻り値として返すことの出来る機能を紹介します。
関数型言語では関数を「戻り値」として返すことができます。
その際に必要となってくる機能/概念が「クロージャ」です。
いきなり関数型プログラミング特有の、専門用語っぽい難しい
言葉が出てきてとまどうかもしれません。
クロージャは噛み砕いて言うと、「関数を返す関数」と、
その関数実行時の、変数などの「環境」が保持される機能を持つものです。
(日本語では「関数閉包」と呼ばれます。)
オブジェクト指向言語に慣れ親しんでいる人なら、感覚的には関数だけで、
「クラスによるメンバ変数のカプセル化」が実現できるもの
と受け止めたら良いと思います。
関数 ⇔ クラス
関数内ローカル変数 ⇔ クラス内メンバ変数
こういうものはサンプルコードを見るのが一番なので、見てみましょう。
---------------- [サンプルコード] ----------------<br /><br />#------------------------------<br /># 呼ばれた回数を返すカウンタ関数。<br /># クロージャを生成する。<br /># init_valは初期値<br />#------------------------------<br />def make_counter(init_val):<br /> # カウンタ変数<br /> # 本来はc=init_valとしたいですが、リストになっているのは<br /> # pythonの言語仕様によるものです。<br /> c = [init_val] # 初期化<br /> #---------------<br /> # 関数内関数の定義<br /> #---------------<br /> def _counter():<br /> c[0] += 1 # 変数cはmake_counter関数のローカル変数を参照する<br /> return c[0]<br /> # 関数内関数を返す<br /> return _counter<br /><br />def main():<br /> # クロージャを生成するカウンタ関数を2つ作成<br /> counter_a = make_counter(0)<br /> counter_b = make_counter(10)<br /> <br /> # それぞれを呼ぶ<br /> print "counter_a: ", counter_a()<br /> print "counter_a: ", counter_a()<br /> print "counter_a: ", counter_a()<br /> <br /> print "counter_b: ", counter_b()<br /> <br /> print "counter_a: ", counter_a()<br /><br /> print "counter_b: ", counter_b()<br /><br />#------------------------------<br /># メイン関数<br />#------------------------------ <br />if __name__ == '__main__':<br /> main()<br /><br />
---------------- [実行結果] ----------------<br />counter_a: 1<br />counter_a: 2<br />counter_a: 3<br />counter_b: 11<br />counter_a: 4<br />counter_b: 12<br />
ここで注目して欲しいのは、_counter関数内で
make_counter関数内のローカル変数cを参照することができ、
しかも_counter関数の呼び出し完了後も保持されることにより
カウンタ変数cが書き換わっていることです。
ここでのmake_counter関数のように、
クロージャを作った関数をエンクロージャと呼びます。
・クロージャは定義されたときの環境を保持する
・エンクロージャが実行された時環境が生成される
・クロージャ内部の変数はそのエンクロージャの環境で解決される。
ことが特徴になります。
この機能により、
1, 変数の影響範囲(スコープ)を関数内に限定し、例えばグローバル変数のようなものを使わなくて済む。
2, クロージャ生成による関数実行時の環境が保持されることで、様々な機能を持つ関数を作成することができる。
といったメリットがあります。2番めについてはピンと来ないと思うので、
次回改めてクロージャを用いた機能や概念を紹介したいと思います![]()
【今回のまとめ】
・関数型言語では関数を返す関数を定義でき、クロージャと呼ばれる機能を持つ。
・クロージャにより、関数内での環境を共有できる関数を生成することができ、
変数のスコープを限定させることができつつ様々な機能を持つ関数を作成できる。
