第3章:クロージャに Challenge!


今回はクロージャです。
JavaScriptではお馴染みですが、Javaプログラマーにとってはどうでしょうか。

一般的なクロージャ定義

まずwikipediaの定義を見てみましょう。

クロージャ(クロージャー、closure、閉包)はプログラミング言語における関数オブジェクトの一種。いくつかの言語ではラムダ式 (Lambda Expression) や無名関数 (Anonymous function) で実現している。
引数以外の変数を実行時の環境ではなく、自身が定義された環境(静的スコープ)において解決することを特徴とする。
関数とそれを評価する環境のペアであるともいえる。

よくわかりませんが、関数オブジェクトの一種だとは読み取れますね。

引数以外の変数を実行時の環境ではなく、自身が定義された環境(静的スコープ)において解決することを特徴とする。

大事なことのように見えますが難しいですね。

クロージャを定義してみる

wikipediaの定義が難しいので、勝手にクロージャを定義してみます。
前提として、関数A・関数Bが存在することとして、以下の条件が必要となります。

  • 関数Aの戻り値が関数B
  • 変数Cは、関数A内で宣言されている
  • 関数B内では身元不明の変数Cを、宣言なしで使える

変数Cを自由変数と呼ぶことにすると、
クロージャとは自由変数を持つ関数と言えます。
この場合クロージャとは関数Bとなります。

良くある例では、カウンタを自由変数として、クロージャでカウントさせる関数があります。
自由変数には外からアクセスできないため、クロージャを使うと変数を隠蔽化できることが
メリットとしてよく挙げられています。

使われる頻度が多いJavaScriptで、クロージャを書いてみます。

JavaScriptでクロージャ

closure.html
<!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の場合と全く同じ動きをするソースです。
コンパクトで良いですね!

Closure.scala
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

まとめ

今回は

体を動かさないと、わかりませんよ。