In this series, I will practice with various subjects with the aim of mastering functional programming of Java
. This time we will cover zipWith
.
zip
and zipWith
zip
is a function that is usually included as standard in functional languages, where the elements of two lists are combined in sequence to create a new list.
Below is an example of Haskell
. The first element 1
and'a'
generate a tuple (1,'a')
, and the second and third elements are processed in the same way, with an overall length of 3. A list of tuples is generated.
zip
Prelude> zip [1,2,3] ['a','b','c']
[(1,'a'),(2,'b'),(3,'c')]
zipWith
is a higher-order function that allows you to specify the function to apply when combining elements.
The following example applies a function called replicate
that replicates a specified element a specified number of times.
zipWith
Prelude> zipWith replicate [1,2,3] ['a','b','c']
["a","bb","ccc"]
zipWith
in Java
At first, for the sake of clarity, I will write a non-general purpose _zipWith
without using generics.
The test code equivalent to the zipWith
code example of Haskell
in the previous section is as follows.
_zipWith test code
@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());
}
The implementation of _zipWith
is as follows.
_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());
}
After that, you can use Generics for generalization.
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());
}
zip
in Java
You can implement zip
using zipWith
.
Java
does not have tuples, so in the code below, java.util.AbstractMap.SimpleEntry
is used instead.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);
}
The function passed to zipWith
is regarded as a function that receives two
arguments and generates a tuple (SimpleEntry in this case), and is described by a lambda expression.
The sample code is uploaded to GitHub.
Recommended Posts