Help us understand the problem. What is going on with this article?

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

More than 5 years have passed since last update.

今回はクロージャです。
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等のブラウザで開いてみましょう。
コンソールに以下を出力します。

closure_result.png

この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

まとめ

今回は

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

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away