LoginSignup
3
4

More than 5 years have passed since last update.

Java 関数型プログラミング練習帳 - zipWith -

Last updated at Posted at 2017-05-28

はじめに

このシリーズでは、Javaの関数型プログラミングを使いこなすことを目的に、いろいろな題材で練習をしていこうと思います。今回はzipWithを取り上げます。

zipzipWith

zipは関数型言語にはたいてい標準で装備されている関数で、2つのリストの要素を順々に組み合わせていって新しいリストを作成するものです。
以下はHaskellの例です。1つ目の要素1'a'によりタプル(1,'a')が生成され、同様に2番目、3番目の要素が処理されて、全体として長さが3のタプルのリストが生成されます。

zip
Prelude> zip [1,2,3] ['a','b','c']
[(1,'a'),(2,'b'),(3,'c')]

zipWithは要素同士を組み合わせる際に適用する関数を指定できる高階関数です。
以下の例では、指定した要素を指定した回数繰り返して複製するreplicateという関数を適用した例です。

zipWith
Prelude> zipWith replicate [1,2,3] ['a','b','c']
["a","bb","ccc"]

JavazipWithを実装

最初はわかりやすさのためジェネリクスを使わずに汎用的でない_zipWithを書いてみます。
前節のHaskellzipWithのコード例と同等のテストコードは以下となります。

_zipWithのテストコード
    @Test
    public void test_zipWith() {
        List<String> l1 = Arrays.asList("a", "b", "c");
        List<Integer> l2 = Arrays.asList(new Integer(1), new Integer(2), new Integer(3));
        BiFunction<String, Integer, String> f = FunctionsTest::replicate;
        List<String> zipped = Functions._zipWith(f, l1, l2);

        assertNotNull(zipped);
        assertEquals(3, zipped.size());
        assertEquals("a", zipped.get(0));
        assertEquals("bb", zipped.get(1));
        assertEquals("ccc", zipped.get(2));
    }

    private static String replicate(String str, Integer n) {
        return IntStream.range(0, n).mapToObj(i -> str).collect(Collectors.joining());
    }

_zipWithの実装は以下のようになります。

_zipWith
    public static List<String> _zipWith(BiFunction<String, Integer, String> f, List<String> l1, List<Integer> l2) {
        final int min;
        if ((l1 == null || l1.size() == 0) || (l2 == null || l2.size() == 0)) {
            return Collections.emptyList();
        } else {
            min = l1.size() <= l2.size() ? l1.size() : l2.size();
        }
        return IntStream.range(0, min)
                .mapToObj(i -> f.apply(l1.get(i), l2.get(i)))
                .collect(Collectors.toList());
    }

あとはジェネリクスを使って汎用化すればよいですね。

zipWith
    public static <T, U, R> List<R> zipWith(BiFunction<T, U, R> f, List<T> l1, List<U> l2) {
        final int min;
        if ((l1 == null || l1.size() == 0) || (l2 == null || l2.size() == 0)) {
            return Collections.emptyList();
        } else {
            min = l1.size() <= l2.size() ? l1.size() : l2.size();
        }
        return IntStream.range(0, min)
                .mapToObj(i -> f.apply(l1.get(i), l2.get(i)))
                .collect(Collectors.toList());
    }

Javazipを実装

zipWithを使用してzipを実装できます。
※残念ながらJavaにはタプルがないので、以下のコードではjava.util.AbstractMap.SimpleEntryで代用しています。

zip
    public static <T, U> List<SimpleEntry<T, U>> zip(List<T> l1, List<U> l2) {
        return zipWith((t, u) -> new SimpleEntry<>(t, u), l1, l2);
    }

zipWithに渡す関数を引数を2つ受け取ってタプル(ここではSimpleEntry)を生成する関数ととらえて、ラムダ式で記述しています。

補足

サンプルコードはGitHubにアップしています。

3
4
0

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
3
4