クロージャってなんぞ
最近、若者の間では、「クロージャ」という言葉がよくでてきますね。
(注意:私の最近は、普通の人の最近ではないと思います)
おじさんも会社の若者の話に混ざりたいので、ちょっと勉強してみましたよ。
以下のサイトの解説がとても親切で分かりやすかったです。
http://dqn.sakusakutto.jp/2009/01/javascript_5.html
勉強したことをメモ書きしておきます。
■問題:クロージャとは何か。
■答え:状態を保持する関数のこと。
状態とは、ほとんどのプログラミング言語において、変数のこと。
変数を状態として、関数内に閉じ込めておくから、クロージャ。
■問題:呼び出すたびに、1,2,3,...を返すような関数f()を定義せよ。
f(); // 1
f(); // 2
f(); // 3
要するに、引数を取らずにこれを実現できるのがクロージャ、ということです。
JavaScriptの場合
クロージャと言えば、JavaScriptの得意技ですね!
おじさんは普段、JavaのようなJava Scriptしか書きませんが、
JavaScriptでは関数もオブジェクトとして扱われることは知っていますよ。
function outer() { var x = 0; return function() {return ++x;}; } var f = outer(); f();
Lispの場合
そう言えば、Lispには遥か昔からラムダというものが存在しましたね。
(defun outer () (let ((x 0)) (defun inner () (incf x)))) (setq f (outer)) (funcall f)
標準C++11の場合
ラムダ式が書けます。かなりスマートだと思います。
以下のコードはVisual Studio 2012で動きました。
Xcodeは手元のバージョンが4.3.3で、こちらではまだ対応していませんでした。
(→代わりに後述のブロック構文が使えます)
Xcode 4.4なら標準C++11に対応していると思います。
std::function<int()> outer() { int x = 0; return [=]() mutable -> int{return ++x;}; } auto f = outer(); f();
Mac拡張のCの場合(ブロック構文)
Mac OS X、iOS上で動くC、C++、Objective-Cではブロック構文が使えます。
int (^outer())() { __block int x = 0; return Block_copy(^{return ++x;}); } int (^f)() = outer(); f(); Block_release(f);
Objective-Cの場合
ブロック構文を使うので、基本的にCの場合と同じですが、関数(ブロック)をオブジェクトとして扱えます。
int (^outer())() { __block int x = 0; return [[^{return ++x;} copy] autorelease]; } int (^f)() = outer(); f();
C#の場合
Func<int> outer() { int x = 0; return () => {++x; return x;}; } Func<int> f = outer(); f();
Javaの場合
あまりスマートではないですが、一応できる・・・のかな。
Javaでは単純な関数というものを作れないのでクラスやインターフェイスでラップすることにします。
これをできていると言っていいのかどうか。
interface I {int f();} I outer() { return new I() { private int x = 0; @Override public int f() { return ++ x; } }; } I i = outer(); i.f();
Pythonの場合
私はPythonをほとんど知りませんが、どう書くのかちょっと興味はあります。
トライしてみましたが・・・が・・・だめ・・・!
ラムダの中で代入できないというところで、はまってしまいました。
そこで、以下の記事を参考にさせてもらいました。
http://d.hatena.ne.jp/amachang/20080304/1204633733
class Scope(object): def __init__(self, parent = None): self.parent = parent self.hash = {} def var(self, key, value = None): self.hash[key] = value return value def set(self, key, value): if key in self.hash or not(self.parent): self.hash[key] = value else: self.parent.set(key, value) return value; def get(self, key): if key in self.hash or not(self.parent): return self.hash[key] else: return self.parent.get(key) def __setattribute__(self, key, value): return self.set(key, value) def __getattr__(self, key): return self.get(key) def outer(): _ = Scope() _.var('x', 0) return lambda:(lambda _: _.var('x', _.x + 1))(_) f = outer() f()
本当にこれしかやり方がないのだろうか・・・
もっとスマートな書き方がありそうですが、今の私にはこれが限界。
もう少しPythonを勉強して、何か分かったらブログに書きたいと思います。
2013/9/20 追記
誰だこんな(↑)クソみたいなコードを書いたのは。
蛇使い式、これスマート
def outer(): x = {"val":0} def inner(): x["val"] += 1 return x["val"] return inner f = outer() print f() # 1 print f() # 2 print f() # 3