28
26

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2013-06-09

今回はクロージャです。
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

まとめ

今回は

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

28
26
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
28
26

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?