Java.use(better);

前の記事次の記事
Java.use(better);


Episode#09

クロージャーの代用表現 -- 配列と別れる50の方法


《関連記事》

《目的》
関数型プログラミングJava で実践するには、クロージャに代わる仕掛が必要です。また、コレクション操作に欠かせない、UML2/OCL に準拠するメソッド群を実現する手法を紹介します。

《動機》
Java を導入したときに悩ましいのは、クロージャーが使えないことでした。遅々として進まない Java の状況に業を煮やして、Jython を導入した(2003年)のもそのためです。また、関数型プログラミングは、プログラミング言語の制約を受けないので、Java でも実践できます。それを経験しておくと、Scala など、関数型プログラミングの機能を備えた言語の理解にも役立ちます。

Java の隘路

コレクションを扱うときの問題点を探るために、数列を事例に話を進めます。以下は、数列を列挙するメソッド range が存在するものとして、次のように、

    System.out.println(range(9));
    ...
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

0 から 9 までの数列が得られます。

□ 事例:collect 操作

数列の各要素を10倍にした、新たな数列が欲しいときには、

  List case_collect1() {
    List seq = new Vector();
    for (int e: range(9)) seq.add(e*10);
    return seq;
  }
  ...
[0, 10, 20, 30, 40, 50, 60, 70, 80, 90]

各要素を追加するときに、式 e*10 を指定します。また、数列の各要素に10を加えた、新たな数列が欲しいときには、

  List case_collect2() {
    List seq = new Vector();
    for (int e: range(9)) seq.add(e+10);
    return seq;
  }
  ...
[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]

各要素を追加するときに、式 e+10 を指定します。

これらの違いは * と + だけです。ところが、残りのコードの断片は、繰り返し記述するしかありません。というのも、メソッド呼び出しの実引数に (e*10) や (e+10) を指定したくても、変数 e は、メソッドの本体でのみ有効です。なにか術はないでしょうか。

同様のことを Scala で実現すると、次のようになります。

scala> print( 0 to 9 )
Range(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
scala> print( 0 to 9 map { _*10 } )
Vector(0, 10, 20, 30, 40, 50, 60, 70, 80, 90)

メソッド map の引数に指定した式 _*10 は任意です。この変数 _ は、数列 0 to 9 の要素を順に参照します。そのため、残りのコードの断片は、他の目的にも再利用できます。また、Jython では、次のようになります。

>>> [e for e in range(10)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> [e*10 for e in range(10)]
[0, 10, 20, 30, 40, 50, 60, 70, 80, 90]

[ に続く式 e*10 は任意です。この変数 e は、数列 range(10) の要素を順に参照します。そのため、残りのコードの断片は、他の目的にも再利用できます。

□ 事例:select 操作

数列の中から偶数だけを選びたいときには、

  static List case_select1() {
    List seq = new Vector();
    for (int e: range(9)) if (e%2 == 0) seq.add(e);
    return seq;
  }
  ...
[0, 2, 4, 6, 8]

各要素を追加するときに、式 e%2==0 を指定します。また、奇数だけを選びたいときには、

  static List case_select2() {
    List seq = new Vector();
    for (int e: range(9)) if (e%2 != 0) seq.add(e);
    return seq;
  }
  ...
[1, 3, 5, 7, 9]

各要素を追加するときに、式 e%2!=0 を指定します。

これらの違いは = と ! だけです。ところが、残りのコードの断片は、繰り返し記述するしかありません。というのも、メソッド呼び出しの実引数に (e%2==0) や (e%2!=0) を指定したくても、変数 e は、メソッドの本体でのみ有効です。なにか術はないでしょうか。

同様のことを Scala で実現すると、次のようになります。

scala> print( 0 to 9 )
Range(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
scala> print( 0 to 9 filter { _%2 == 0 } )
Vector(0, 2, 4, 6, 8)

メソッド filter の引数に指定した条件式 _%2==0 は任意です。この変数 _ は、数列 0 to 9 の要素を順に参照します。そのため、残りのコードの断片は、他の目的にも再利用できます。また、Jython では、次のようになります。

>>> [e for e in range(10)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> [e for e in range(10) if e%2 == 0]
[0, 2, 4, 6, 8]

if に続く条件式 e%2==0 は任意です。この変数 e は、数列 range(10) の要素を順に参照します。そのため、残りのコードの断片は、他の目的にも再利用できます。

 ↑ TOP

》作業中です《

update*13/01/30 20:09:44

♪ 早乙女(さおとめ)



出典 ☞ http://shimayu.co.jp/modules/myalbum/photo.php?lid=96