関数型Javaの紹介

関数型Javaの概要

1. 概要

このチュートリアルでは、Functional Javaライブラリの概要といくつかの例を紹介します。

2. 関数型Javaライブラリ

Functional Javaライブラリーは、Javaでの関数型プログラミングを容易にするためのオープンソースライブラリです。 ライブラリは、Functional Programmingで一般的に使用される多くの基本的および高度なプログラミング抽象化を提供します。

ライブラリの機能の多くは、Fインターフェイスを中心に展開しています。 This F interface models a function that takes an input of type A and returns an output of type B.これらはすべて、Java独自の型システムの上に構築されています。

3. Mavenの依存関係

まず、必要なdependenciespom.xmlファイルに追加する必要があります。


    org.functionaljava
    functionaljava
    4.8.1


    org.functionaljava
    functionaljava-java8
    4.8.1


    org.functionaljava
    functionaljava-quickcheck
    4.8.1


    org.functionaljava
    functionaljava-java-core
    4.8.1

4. 関数を定義する

後で例で使用できる関数を作成することから始めましょう。

Functional Javaがなければ、基本的な乗算方法は次のようになります。

public static final Integer timesTwoRegular(Integer i) {
    return i * 2;
}

Functional Javaライブラリーを使用して、この機能をもう少しエレガントに定義できます。

public static final F timesTwo = i -> i * 2;

上記のように、Integerを入力として受け取り、そのInteger x 2を出力として返すFインターフェースの例を示します。

これは、入力としてIntegerを受け取る基本関数の別の例ですが、この場合、入力が偶数か奇数かを示すためにBooleanを返します。

public static final F isEven = i -> i % 2 == 0;

5. 関数の適用

関数が配置されたので、それらをデータセットに適用しましょう。

Functional Javaライブラリーは、リスト、セット、配列、マップなどのデータを管理するための通常のタイプのセットを提供します。 The key thing to realize is that these data types are immutable.

さらに、ライブラリは必要に応じてconvenience functions to convert to and from standard Java Collections classesを提供します。

以下の例では、整数のリストを定義し、それにtimesTwo関数を適用します。 同じ関数のインライン定義を使用してmapも呼び出します。 もちろん、結果は同じであると予想されます。

public void multiplyNumbers_givenIntList_returnTrue() {
    List fList = List.list(1, 2, 3, 4);
    List fList1 = fList.map(timesTwo);
    List fList2 = fList.map(i -> i * 2);

    assertTrue(fList1.equals(fList2));
}

ご覧のとおり、mapは同じサイズのリストを返します。ここで、各要素の値は、関数が適用された入力リストの値です。 入力リスト自体は変更されません。

isEven関数を使用した同様の例を次に示します。

public void calculateEvenNumbers_givenIntList_returnTrue() {
    List fList = List.list(3, 4, 5, 6);
    List evenList = fList.map(isEven);
    List evenListTrueResult = List.list(false, true, false, true);

    assertTrue(evenList.equals(evenListTrueResult));
}

Since the map method returns a list, we can apply another function to its output.map関数を呼び出す順序によって、結果の出力が変わります。

public void applyMultipleFunctions_givenIntList_returnFalse() {
    List fList = List.list(1, 2, 3, 4);
    List fList1 = fList.map(timesTwo).map(plusOne);
    List fList2 = fList.map(plusOne).map(timesTwo);

    assertFalse(fList1.equals(fList2));
}

上記のリストの出力は次のとおりです。

List(3,5,7,9)
List(4,6,8,10)

6. 関数を使用したフィルタリング

関数型プログラミングでよく使用されるもう1つの操作は、take an input and filter out data based on some criteriaです。 そして、おそらくすでにお察しのとおり、これらのフィルタリング基準は関数の形式で提供されます。 この関数は、データを出力に含める必要があるかどうかを示すブール値を返す必要があります。

次に、isEven関数を使用して、filterメソッドを使用して入力配列から奇数を除外します。

public void filterList_givenIntList_returnResult() {
    Array array = Array.array(3, 4, 5, 6);
    Array filteredArray = array.filter(isEven);
    Array result = Array.array(4, 6);

    assertTrue(filteredArray.equals(result));
}

興味深い観察の1つは、この例では、前の例で使用したListの代わりにArrayを使用し、関数が正常に機能したことです。 Because of the way functions are abstracted and executed, they do not need to be aware of what method was used to collect the input and output.

この例では、独自のisEven関数も使用しましたが、Functional Javaの独自のIntegerクラスにもbasic numerical comparisonsの標準関数があります。

7. 関数を使用したブール論理の適用

関数型プログラミングでは、「すべての要素が何らかの条件を満たす場合にのみこれを行う」、「少なくとも1つの要素が何らかの条件を満たす場合にのみこれを行う」などのロジックを頻繁に使用します。

関数型Javaライブラリは、existsメソッドとforallメソッドを介してこのロジックのショートカットを提供します。

public void checkForLowerCase_givenStringArray_returnResult() {
    Array array = Array.array("Welcome", "To", "example");
    assertTrue(array.exists(s -> List.fromString(s).forall(Characters.isLowerCase)));

    Array array2 = Array.array("Welcome", "To", "example");
    assertFalse(array2.exists(s -> List.fromString(s).forall(Characters.isLowerCase)));

    assertFalse(array.forall(s -> List.fromString(s).forall(Characters.isLowerCase)));
}

上記の例では、文字列の配列を入力として使用しました。 fromString関数を呼び出すと、配列の各文字列が文字のリストに変換されます。 これらの各リストに、forall(Characters.isLowerCase)を適用しました。

ご想像のとおり、Characters.isLowerCaseは、文字が小文字の場合にtrueを返す関数です。 したがって、forall(Characters.isLowerCase)を文字のリストに適用すると、リスト全体が小文字で構成されている場合にのみtrueが返されます。これは、元の文字列がすべて小文字であることを示します。

最初の2つのテストでは、少なくとも1つの文字列が小文字であるかどうかだけを知りたいので、existsを使用しました。 3番目のテストでは、forallを使用して、すべての文字列が小文字であるかどうかを確認しました。

8. 関数によるオプション値の処理

コードでオプションの値を処理するには、通常、== nullまたはisNotBlankのチェックが必要です。 Java 8は、これらのチェックをよりエレガントに処理するためにOptionalクラスを提供し、Functional Javaライブラリは、Optionクラスを介して欠落データを適切に処理するための同様の構造を提供します。

public void checkOptions_givenOptions_returnResult() {
    Option n1 = Option.some(1);
    Option n2 = Option.some(2);
    Option n3 = Option.none();

    F> function = i -> i % 2 == 0 ? Option.some(i + 100) : Option.none();

    Option result1 = n1.bind(function);
    Option result2 = n2.bind(function);
    Option result3 = n3.bind(function);

    assertEquals(Option.none(), result1);
    assertEquals(Option.some(102), result2);
    assertEquals(Option.none(), result3);
}

9. 関数を使用して集合を減らす

最後に、セットを削減する機能を見ていきます。 「セットの削減」とは、「セットを1つの値にロールアップする」と言うのが凝った方法です。

The Functional Java library refers to this functionality as folding

関数を指定して、要素を折り畳むことの意味を示す必要があります。 この例は、配列またはリスト内の整数を追加する必要があることを示すIntegers.add関数です。

関数が折り畳むときの機能に基づいて、右から折り畳むか左から折り畳むかによって結果が異なる場合があります。 これが、FunctionalJavaライブラリが両方のバージョンを提供する理由です。

public void foldLeft_givenArray_returnResult() {
    Array intArray = Array.array(17, 44, 67, 2, 22, 80, 1, 27);

    int sumAll = intArray.foldLeft(Integers.add, 0);
    assertEquals(260, sumAll);

    int sumEven = intArray.filter(isEven).foldLeft(Integers.add, 0);
    assertEquals(148, sumEven);
}

最初のfoldLeftは、単にすべての整数を加算します。 一方、2番目は最初にフィルターを適用してから、残りの整数を追加します。

10. 結論

この記事は、Functional Javaライブラリーの簡単な紹介です。

いつものように、記事の完全なソースコードはover on GitHubで入手できます。