今回はクロージャです。
JavaScriptではお馴染みですが、Javaプログラマーにとってはどうでしょうか。
一般的なクロージャ定義
まずwikipediaの定義を見てみましょう。
クロージャ(クロージャー、closure、閉包)はプログラミング言語における関数オブジェクトの一種。いくつかの言語ではラムダ式 (Lambda Expression) や無名関数 (Anonymous function) で実現している。
引数以外の変数を実行時の環境ではなく、自身が定義された環境(静的スコープ)において解決することを特徴とする。
関数とそれを評価する環境のペアであるともいえる。
よくわかりませんが、関数オブジェクトの一種だとは読み取れますね。
引数以外の変数を実行時の環境ではなく、自身が定義された環境(静的スコープ)において解決することを特徴とする。
大事なことのように見えますが難しいですね。
クロージャを定義してみる
wikipediaの定義が難しいので、勝手にクロージャを定義してみます。
前提として、関数A・関数Bが存在することとして、以下の条件が必要となります。
- 関数Aの戻り値が関数B
- 変数Cは、関数A内で宣言されている
- 関数B内では身元不明の変数Cを、宣言なしで使える
変数Cを__自由変数__と呼ぶことにすると、
クロージャとは__自由変数を持つ関数__と言えます。
この場合クロージャとは関数Bとなります。
良くある例では、カウンタを自由変数として、クロージャでカウントさせる関数があります。
自由変数には外からアクセスできないため、クロージャを使うと変数を隠蔽化できることが
メリットとしてよく挙げられています。
使われる頻度が多いJavaScriptで、クロージャを書いてみます。
JavaScriptでクロージャ
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>closure</title>
</head>
<body>
<p>Hello World</p>
<script type="text/javascript">
<!-- リスト -->
var candidates = new Array(1,2,3,4,5);
<!-- フィルター関数 -->
var filter = function (predicate) {
return function(candidates) {
var numbers = new Array();
for(var i = 0; i < candidates.length;i++) {
if(predicate(candidates[i])) {
numbers.push(candidates[i])
}
}
return numbers;
};
};
var predicate = function(x) {
return (x % 2) == 0;
};
<!-- 偶数フィルター関数 -->
var oddFilter = filter(predicate)
var results = oddFilter(candidates);
<!-- 結果をコンソールに出力 -->
for(var i = 0; i < results.length;i++) {
console.log(results[i]);
}
</script>
</body>
</html>
このHTMLファイルをChrome等のブラウザで開いてみましょう。
コンソールに以下を出力します。
このJavaScriptのソースで、クロージャを実現している部分がわかりますか?
__自由変数を持つ関数__です。
そうです、変数filter
で表している関数オブジェクトの戻り値となっている
無名関数がクロージャです。
ここでは、predicate
が自由変数となります。
ソースの内容
簡単に説明すると、1~5までの数字が入った配列から偶数を抽出して
コンソールに出力するようになっています。
各関数の説明も簡単に記述します。
filter
関数
引数の配列から、ある条件に一致する要素を抽出する関数。
一番複雑ですが、一番のキモです。
var filter = function (predicate) {
return function(candidates) {
var numbers = new Array();
for(var i = 0; i < candidates.length;i++) {
if(predicate(candidates[i])) {
numbers.push(candidates[i])
}
}
return numbers;
};
};
難しいことをやっていませんが、無名関数の中のpredicate
関数は
自由変数であることを意識して下さい。
大事なことなので繰り返します。
predicate
関数
引数が偶数か判定する。偶数の場合はtrue、奇数の場合はfalseを返す。
var predicate = function(x) {
return (x % 2) == 0;
};
oddFilter
関数
filter
関数とpredicate
関数から成り立つ。
var oddFilter = filter(predicate)
Scalaでクロージャ
JavaScriptの場合と全く同じ動きをするソースです。
コンパクトで良いですね!
object Closure{
def main(args: Array[String]){
val candidates = List(1,2,3,4,5)
val filter = (predicate :Int => Boolean) => {
(candidates :List[Int]) => {
for(x <- candidates; if predicate(x)) yield x
}
}
val predicate = (x:Int) => x % 2 ==0
var oddFilter = filter(predicate)
oddFilter(candidates).foreach(println)
}
}
実行すると以下のようになります。
$ scala Closure.scala
2
4
JavaScriptのソースを見ながらScalarでの実現の仕方を探ってみて下さい。
クロージャを実装している代表的な言語
- Scheme
- Common Lisp
- Haskell
- OCaml
- Smalltalk
- ECMAScript
- Groovy
- Perl
- Python
- Ruby
- PHP(5.3以降)
- C++(11から)
- Objective-C
クロージャを実装していない代表的な言語
- Java(8で実装される予定)
- C
まとめ
今回は
体を動かさないと、わかりませんよ。