クロージャってなんぞ

最近、若者の間では、「クロージャ」という言葉がよくでてきますね。
(注意:私の最近は、普通の人の最近ではないと思います)
おじさんも会社の若者の話に混ざりたいので、ちょっと勉強してみましたよ。

以下のサイトの解説がとても親切で分かりやすかったです。

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 XiOS上で動く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#の場合

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();


結局Java 7にはラムダ式は導入されなかったそうで。
Java 8ではどうなんでしょうか。


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

Rubyの場合

2013/2/17 追記
初めてのRuby P135に答えそのものが載っていました。

def outer
    x = 0
    return Proc.new do
        x += 1
    end
end

f = outer
f.call

おしまい

だいたいどの言語でもクロージャは実現できそうだと分かりました。
Rubyはこれから勉強するので、後から追記します。

個人的にはC++ラムダ式がふつくしく感じます。
対してJavaときたら...

Javaくんってほんと、のろくてどんくいんだからっ!」

と、クラスの世話焼きの女子におせっかいされそうな...

うらやましいじゃねえかちくしょう!



ともあれ、

これで明日から、会社の若者にどや顔で「クロージャ」を語れます!
やったね、おじさん!