- Java言語でのプログラミングの機能スタイルのアプリケーションについて。
- Javaの例で関数型プログラミングからのデータのコレクションを操作するためのいくつかの基本的なパターンについて。
- Googleコレクションライブラリについて少し。
Java、C#、C ++、PHP、またはその他のオブジェクト指向言語でプログラミングする場合、関数型プログラミングに精通したいが、Haskell / Scala / Lisp / Pythonを学習する能力/欲求を持っていない場合、この記事は特にあなたに適しています。
関数型プログラミングに精通しているが、Javaで関数プログラミングを使用したことがない人は、関数型プログラミングも面白いと思う。
Java言語での「関数」の構築を紹介します
関数型プログラミングとは何ですか? 一言で言えば、関数型プログラミングとは、関数がオブジェクトであり、変数に代入したり、他の関数に引数として渡したり、関数から結果として返されるなどのプログラミングのことです。少し後で理解します。 今のところ、Javaで「関数」コンストラクト自体を使用する方法を理解する必要があります。
ご存じのとおり、Javaには関数はなく、クラス、メソッド、クラスオブジェクトのみがあります。 しかし、Javaには匿名クラスがあります。つまり、メソッドのコードで直接宣言できる名前のないクラスです。 これが使用するものです。 まず、このインターフェイスを宣言します。
public final interface Function<F, T> { T apply(F from); }
メソッドのコードで、このインターフェイスの匿名実装を宣言できます。
public static void main() { // "", intToString. Function<Integer, String> intToString = new Function<Integer, String>() { @Override public String apply(Integer from) { return from.toString(); } }; intToString.apply(9000); // . "9000". }
このような実装を「匿名関数」と呼びます。 関数型プログラミングの観点からは、関数型言語の関数と同じことを行うことができます:変数に割り当て、他の関数(およびクラスメソッド)に引数として渡し、関数(およびクラスメソッド)から結果として取得します。
次に、関数型プログラミングのいくつかの基本的なパターンの提示に移ります。
機能的なスタイルのコレクション
整数のコレクションがあるとします。 それらを文字列として表示したいので、文字列内の各数字はコンマで区切られます。 機能しないソリューションは次のようになります。
public String joinNumbers(Collection<? extends Integer> numbers) { StringBuilder result = new StringBuilder(); boolean first = true; for (Integer number : numbers) { if (first) first = false; else result.append(", "); result.append(number); } return result; }
機能的なソリューションを実装するには、最初にいくつかの関数とメソッドを準備する必要があります。 これらをクラスの静的フィールドとして宣言します。
public static final Function<Integer, String> INT_TO_STRING = ... // // from, transformer // . public static <F, T> List<T> map(Collection<F> from, Function<? super F,? extends T> transformer) { ArrayList<T> result = new ArrayList<T>(); for (F element : from) result.add(transformer.apply(element)); return result; } // public static <T> String join(Collection<T> from, String separator) { StringBuilder result = new StringBuilder(); boolean first = true; for (T element : from) { if (first) first = false; else result.append(separator); result.append(element); } return result.toString(); }
これで、joinNumbersメソッドは次のようになります。
public String joinNumbers(Collection<? extends Integer> numbers) { return join(map(numbers, INT_TO_STRING), ", "); }
このメソッドは、正確に1行で実装されます。
私はいくつかの重要な点に注意したいと思います:
-
map
およびjoin
メソッドは非常に一般化されています。つまり、これらのメソッドはこの問題を解決するためだけに使用することはできません。 これは、それらをユーティリティクラスに分離し、プロジェクトのさまざまな部分でこのクラスを使用できることを意味します。 -
map
メソッドのCollection
クラスの代わりに、Iterable
を渡して新しいIterable
を返し、返されたコレクションのデータをクロールするときに転送されたコレクションからデータを取得します。 このような実装により、たとえば、データ変換のチェーンを作成し、変換の各段階を個別の単純な関数として強調表示できますが、アルゴリズムの効率はO(n)のオーダーのままです。
map(map(numbers, MULTIPLY_X_2), INT_TO_STRING); // .
- クラスを作成するとき、そのメソッドのいくつかに対して静的フィールドを作成できます。静的フィールドは、対応するクラスメソッドの呼び出しに適用呼び出しを委任するラッパー関数です。 これにより、たとえば上記の構成で、機能的なスタイルでオブジェクトの「メソッド」を使用できます。
Googleコレクションを使用してコレクションを操作する
Googleのスタッフは、機能的なスタイルでJavaのコレクションを操作できるユーティリティクラスを備えた便利なライブラリを作成しました。 以下が提供する機能の一部です。
-
interface Function<F, T>
上で挙げたものに似たインターフェース。 -
Iterables.filter
。 コレクションと述語関数(ブール値を返す関数)を受け取ります。 応答として、指定された関数がtrueを返した元のすべての要素を含むコレクションを返します。 たとえば、コレクションからすべての偶数をIterables.filter(numbers, IS_ODD);
:Iterables.filter(numbers, IS_ODD);
-
Iterables.transform
。 上記の私の例のマップ関数と同じことをします。 -
Functions.compose
。 2つの機能が必要です。 新しい関数を返します-その構成、つまり要素を受け取る関数は、それを2番目の関数に渡し、結果は最初の関数に渡し、最初の関数から取得した結果はユーザーに戻ります。 コンポジションは、たとえば次のように使用できますIterables.transform(numbers, Functions.compose(INT_TO_STRING, MULTIPLY_X_2));
もちろん、関数型プログラミングや命令型のコレクションを操作するために、Googleコレクションには他にも多くの便利な機能があります。
参照資料
- 関数型プログラミングに関するウィキペディアの記事 。
- Google Guava 、Googleコレクションが含まれるプロジェクト。
- Joshua BlochによるGoogleコレクションのビデオプレゼンテーション 。
- Apache Commonsコレクション 。 これは、Googleコレクションと同様の問題を解決しますが、Java 4の下で、つまりパラメトリックタイプなしで記述されました。
もっと伝えたいこと
親愛なる友人たち、私の記事が気に入ったら、Javaやその他の命令型言語での関数型プログラミングの使用について興味深いことを書いていただければ幸いです。 話したいことがいくつかありますが、1つの記事ですぐに説明する方法はありません。
- 突然変異および免疫回路。
- パターンマッチャー。
- モナド。
- 機能的アプローチを使用した並列化。
- パーサーコンビネーター。
ご意見やご提案をお待ちしております。