Chaining Functional Interfaces - IntUnaryOperator vs UnaryOperator - java

I'm still learning functional interfaces. I'd like to know why I'm able to chain a UnaryOperator to the end of a Function, but not an IntUnaryOperator to the end of the same Function.
UnaryOperator <String> twoOfMe = s -> s + s;
Function <String, Integer> convertMe = s -> Integer.parseInt (s);
UnaryOperator <Integer> twiceMe = n -> 2*n;
IntUnaryOperator doubleMe = n -> 2*n;
int a = twoOfMe.andThen(convertMe).andThen(twiceMe).apply ("2");
int b = twoOfMe.andThen(convertMe).andThen(doubleMe).apply ("2");
int a works with twiceMe but int b doesn't work with the doubleMe.
Thanks
Edit:
It says incompatible types. Required int. Found java.lang.Object

andThen(Function<? super R, ? extends V> after) expects a Function argument. UnaryOperator<Integer> is a sub-interface of Function<Integer,Integer>, which matches. IntUnaryOperator has no relation to the Function interface, so doubleMe cannot be passed to andThen.

This is because IntUnaryOperator maps an int to an int, and Function <String, Integer> convertMe is returning an Integer, not an int. You cannot chain them.
You can declare ToIntFunction<String> convertMe = Integer::parseInt;, then you could chain them.
UnaryOperator<String> twoOfMe = s -> s + s;
ToIntFunction<String> convertMe = Integer::parseInt;
IntUnaryOperator doubleMe = n -> 2*n;
int b = twoOfMe.andThen(convertMe).andThen(doubleMe).apply ("2");

If you take a look into IntUnaryOperator class you will see that it doesn't extend any interfaces and on the other hand UnaryOperator does extend Function interface.
The code you provided doesn't work with IntUnaryOperator because it expects Function which is not what the IntUnaryOperator really is.

Related

Why Arrays.sort(T[] a, Comparator<? super T> c) infers T as Object for a 2d array?

Say if I want to sort a 2d array. (just reorder the rows, don't touch data within each row).
In following snippet: all 3 cases use the same Arrays.sort(T[] a, Comparator<? super T> c) method signature. Case (a) works fine. However, just by adding a if condition to the second argument, the inference of T changes. I couldn't comprehend why.
// array contains 3 tuples, sort it by the first element, then second element
int[][] array1 = new int[3][2];
array1[0] = new int[]{1,2};
array1[1] = new int[]{2,3};
array1[2] = new int[]{2,4};
// Case (a): compiles good, tuple is inferred as int[]
Arrays.sort(array1, Comparator.comparingInt(tuple -> tuple[0])); // Arrays.sort(T[] a, Comparator<? super T> c) correctly infers that T refers to int[]
// Case (b.1): compile error: incompatible types
// tuple is now inferred as Object, why?
Arrays.sort(array1,
(a1, a2) -> a1[0] == a2[0] ?
Comparator.comparingInt(tuple -> tuple[1]) : Comparator.comparingInt(tuple -> tuple[0]));
// Case (b.2): compile error: incompatible types
Arrays.sort(array1, Comparator.comparingInt(tuple -> tuple[0]).thenComparingInt(tuple -> tuple[1]));
// Case (c): if downcast tuple[0] to ((int[])tuple)[0], then (b) works fine.
Update:
Enlightened by the comments, I soon realized that case (b.1) is actual not valid.
The lambda in (b.1) suppose to return an integer, not a comparator. E.g.
Arrays.sort(array1, (a1, a2) -> a1[0] == a2[0] ? 0 : 1);
In all other scenarios, I see Comparator.<int[]>comparingInt(...) forces the inference correctly.
Short answer: the compiler is not smart enough to infer through such complex expressions. It needs some help inferring the type:
Arrays.sort(array1, Comparator.<int[]>comparingInt(tuple -> tuple[0]).thenComparingInt(tuple -> tuple[1]));
Related JEP: http://openjdk.java.net/jeps/101
As for the ternary expression case, I think it needs further adaptation, since you need to return an int in the lambda, not a Comparator:
Arrays.sort(array1,
(a1, a2) -> a1[0] == a2[0] ?
Comparator.<int[]>comparingInt(tuple -> tuple[1]).compare(a1, a2) :
Comparator.<int[]>comparingInt(tuple -> tuple[0]).compare(a1, a2));
After digging a bit further, here's an intuitive explanation on this behavior:
public interface Comparator<T> {
...
default Comparator<T> thenComparingInt(ToIntFunction<? super T> keyExtractor)
public static <T> Comparator<T> comparingInt(ToIntFunction<? super T> keyExtractor)
...
}
The public static method needs the caller to define T (as goes by the first <T> in the second method) when invoking the method. Otherwise it defaults to Object.
This is unlike the non-static method, where T is already defined during instantiation.
So looking at Comparator.comparingInt(tuple -> tuple[0]);, compiler won't know what T is and hence defaults to Object.
To properly feed compiler the actual type T, we can explicitly define T in different places.
// define T when calling the method
Comparator.<int[]>comparingInt(tuple -> tuple[0]);
// infer by return type, this is samilar to placing the rhs to Arrays.sort(int[][] a, rhs)
Comparator<int[]> c = Comparator.comparingInt(tuple -> tuple[0]);
// explicit target type for lambda expression
Comparator.comparingInt((ToIntFunction<int[]>) tuple -> tuple[0]);
// explicit type for lambda parameter
Comparator.comparingInt((int[] tuple) -> tuple[0]);
// This works but it's doesn't change the inference of T as comments suggests below.
Comparator.comparingInt(tuple -> ((int[])tuple)[0]);
Once it returns a Comparator<int[]> object (thus defined T as int[]), the instance method thenComparingInt is called upon knowing what T is.

Is it possible to chain predicates with `and` using different type parameter?

I am learning Java 8 lambdas. Can I join two predicates, with different type parameter, using and method in Predicate interface?
This is my code:
Predicate<Integer> pc = (iv) -> iv > 20;
Predicate<String> pL = (is) -> is.length() > 5;
System.out.println("The predicate result for pc is: " + pc.test(25));
System.out.println("===========");
System.out.println("The predicate result with both condition true: " + pc.and(pL.test("abcd")));
No, you can not chain predicates of different types, unless the chained predicate also accepts the type of the original predicate.
Looking at the signature, you can easily see that:
and(Predicate<? super T> other)
or(Predicate<? super T> other)
You can chain predicates:
Predicate<Person> isMale = p -> p.isMale();
Predicate<Person> isAdult = p -> p.age() >= AGE_OF_MATURITY;
Predicate<Person> isAdultMale = isAdult.and(isMale);
You can only chain predicates that accept at least (thats what the ? super T says) the same type as the original predicate:
Predicate<Object> hasWeirdHashCode = o -> o.hashCode() == 0;
Predicate<Person> nonsense = isMale.and(hasWeirdHashCode);
If you want to test different types (A, B), you need to provide them separately:
Predicate<A> propertyOfA = [...];
Predicate<B> propertyOfB = [...];
BiPredicate<A,B> propertyOfAnB = (a, b) ->
propertyOfA.test(a) && propertyOfB.test(b);
If you need more than two different types, you need to roll your own, custom TriPredicate, QuadPredicate and so on functional interfaces, which should be straight-forward to implement.

Java how to pass a BiFunction as a parameter

I need help figuring out how to pass this BiFunction
BiFunction<Integer, List<Integer>, Integer> func = (a, b) -> {
int result = 0;
int temp = 0;
for(Integer ele : b) {
temp = b.get(ele);
result += temp;
}
return a + result;
};
I am using this junit test
void testFoldLeft() {
LinkedList<Integer> l = new LinkedList<>();
for(int i = 0; i < 10; i++) l.addFirst(i+1);
Integer u = fp.foldLeft(0, l, func(0,l));
}
I am attempting to pass the BiFunction func through foldLeft, which looks like this
static <U,V> V foldLeft(V e, Iterable<U>l, BiFunction<V,U,V> f){
return null;
}
What func is supposed to do is to take a list, in this case b, sum up all the elements in b then add that number to a and return the result. However, Eclipse gives me an error stating that func is undefined. I'm new to BiFunctions so I'm kind of stuck here. I would appreciate any help.
There are two issues in your code:
func(0,l) is a syntax error, just pass func variable as the BiFunction. No need for providing arguments:
Integer u = foldLeft(0, l, func);
The generics signature of foldLeft does not match the BiFunction. Given the current way it is written, Iterable<U> l makes the compiler infer U as Integer, hence the compiler expects the third argument to be a BiFunction<Integer, Integer, Integer> instead of BiFunction<Integer, List<Integer>, Integer>. To make U match the LinkedList<Integer>, declare it as follows:
static <U extends Iterable<V>, V> V foldLeft(V e, U l, BiFunction<V, U, V> f){
return null;
}
Side note: I wonder what the function code is doing:
for(Integer ele : b) {
temp = b.get(ele);
result += temp;
}
This is looping over elements of b and treating them as indices. I doubt this is what you want.
I think the mismatch is caused by an incorrect declaration of the second parameter of foldLeft. If, in your example, Integer is the Iterable's argument, then your method should be declared like this:
static <U, V> V foldLeft(V e, Iterable<V> l, BiFunction<V, U, V> f) {
return null;
}
V is the element type of the iterable, not U (which, in this case, would be equivalent to Iterable<V>).
Further, your invocation should look like this:
Integer u = foldLeft(0, l, func);
You can't invoke an expression in Java. Unless func is declared as a method in visible scope, func() will always be invalid.
Note that you can simplify your generics by getting rid of U (I don't see a specific need for the type variable for the list type here, but I may be missing some use cases):
static <V> V foldLeft(V e, Iterable<V> l, BiFunction<V, Iterable<V>, V> f) {
return null;
}
You do not show your implementation of foldLeft, but I'm sure it's going to need to iterate over the list, or at least pass it to f.apply. So I suppose you can make the function take a List<V>:
static <V> V foldLeft(V e, List<V> l, BiFunction<V, List<V>, V> f) {
return f.apply(e, l);
}
Otherwise, calling f.apply(e, l) would fail as Function<Integer, LinkedList<Integer>, Integer> and Function<Integer, Iterable<Integer>, Integer> are incompatible.

Which FunctionalInterface should I use?

I was learning to write some lambda representation as FunctionalInterface.
So, to add two integers I used:
BiFunction<Integer, Integer, Integer> biFunction = (a, b) -> a + b;
System.out.println(biFunction.apply(10, 60));
Gives me the output 70. But if I write it as this
BinaryOperator<Integer, Integer, Integer> binaryOperator = (a, b) -> a + b;
I get an error saying
Wrong number of type arguments: 3; required: 1
Isn't BinaryOperator a child of BinaryFunction? How do I improve it?
BinaryOperator
Since BinaryOperator works on a single type of operands and result. i.e. BinaryOperator<T>.
Isn't BinaryOperator a child of BinaryFunction?
Yes. BinaryOperator does extends BiFunction.
But do note the documentation states(formatting mine):
This is a specialization of BiFunction for the case where the
operands and the result are all of the same type.
The complete representation is as:
BinaryOperator<T> extends BiFunction<T,T,T>
hence your code shall work with
BinaryOperator<Integer> binaryOperator = (a, b) -> a + b;
System.out.println(binaryOperator.apply(10, 60));
IntBinaryOperator
If you're supposed to be dealing with two primitive integers as currently in your example (add two integers I used), you can make use of the IntBinaryOperator FunctionalInterface as
IntBinaryOperator intBinaryOperator = (a, b) -> a + b;
System.out.println(intBinaryOperator.applyAsInt(10, 60));
Represents an operation upon two int-valued operands and producing
an int-valued result. This is the primitive type specialization of
BinaryOperator for int.
I am using Integer, can I still use IntBinaryOperator
Yes, you can still use it but notice the representation of the IntBinaryOperator
Integer first = 10;
Integer second = 60;
IntBinaryOperator intBinaryOperator = new IntBinaryOperator() {
#Override
public int applyAsInt(int a, int b) {
return Integer.sum(a, b);
}
};
Integer result = intBinaryOperator.applyAsInt(first, second);
would incur you an overhead of unboxing first and second to primitives and then autoboxing the sum as an output to result of type Integer.
Note: Be careful of using null-safe values for the Integer though or else you would probably end up with a NullPointerException.
BiFunction<Integer, Integer, Integer> biFunction = (a, b) -> a + b;
can be represented by
BinaryOperator<Integer> binaryOperator = (a, b) -> a + b;
But generally you want to perform arithmetical computations on int and not Integer in order to avoid unboxing to compute (Integer to int) and boxing again to return the result (int to Integer) :
IntBinaryOperator intBinaryOperator = (a, b) -> a + b;
As a side note, you could also use a method reference instead of a lambda to compute a sum between two ints.
Integer.sum(int a, int b) is what you are looking for :
IntBinaryOperator biFunction = Integer::sum;
Isn't BinaryOperator a child of BinaryFunction?
Yes, it is. If you look at source code of BinaryOperator, you see:
public interface BinaryOperator<T> extends BiFunction<T,T,T> {
// ...
}
So you just have to fix your syntax:
BinaryOperator<Integer> binaryOperator = (a, b) -> a + b;
System.out.println(binaryOperator.apply(10, 60));
How do I improve it?
You can use IntBinaryOperator. It simplifies sytax even more:
IntBinaryOperator binaryOperator = (a, b) -> a + b;
System.out.println(binaryOperator.applyAsInt(10, 60));

Equivalent of Scala's foldLeft in Java 8

What is the equivalent of of Scala's great foldLeft in Java 8?
I was tempted to think it was reduce, but reduce has to return something of identical type to what it reduces on.
Example:
import java.util.List;
public class Foo {
// this method works pretty well
public int sum(List<Integer> numbers) {
return numbers.stream()
.reduce(0, (acc, n) -> (acc + n));
}
// this method makes the file not compile
public String concatenate(List<Character> chars) {
return chars.stream()
.reduce(new StringBuilder(""), (acc, c) -> acc.append(c)).toString();
}
}
The problem in the code above is the accumulator: new StringBuilder("")
Thus, could anyone point me to the proper equivalent of the foldLeft/fix my code?
There is no equivalent of foldLeft in Java 8's Stream API. As others noted, reduce(identity, accumulator, combiner) comes close, but it's not equivalent with foldLeft because it requires the resulting type B to combine with itself and be associative (in other terms, be monoid-like), a property that not every type has.
There is also an enhancement request for this: add Stream.foldLeft() terminal operation
To see why reduce won't work, consider the following code, where you intend to execute a series of arithmetic operations starting with given number:
val arithOps = List(('+', 1), ('*', 4), ('-', 2), ('/', 5))
val fun: (Int, (Char, Int)) => Int = {
case (x, ('+', y)) => x + y
case (x, ('-', y)) => x - y
case (x, ('*', y)) => x * y
case (x, ('/', y)) => x / y
}
val number = 2
arithOps.foldLeft(number)(fun) // ((2 + 1) * 4 - 2) / 5
If you tried writing reduce(2, fun, combine), what combiner function could you pass that combines two numbers? Adding the two numbers together clearly does not solve it. Also, the value 2 is clearly not an identity element.
Note that no operation that requires a sequential execution can be expressed in terms of reduce. foldLeft is actually more generic than reduce: you can implement reduce with foldLeft but you cannot implement foldLeft with reduce.
Update:
Here is initial attempt to get your code fixed:
public static String concatenate(List<Character> chars) {
return chars
.stream()
.reduce(new StringBuilder(),
StringBuilder::append,
StringBuilder::append).toString();
}
It uses the following reduce method:
<U> U reduce(U identity,
BiFunction<U, ? super T, U> accumulator,
BinaryOperator<U> combiner);
It may sound confusing but if you look at the javadocs there is a nice explanation that may help you quickly grasp the details. The reduction is equivalent to the following code:
U result = identity;
for (T element : this stream)
result = accumulator.apply(result, element)
return result;
For a more in-depth explanation please check this source.
This usage is not correct though because it violates the contract of reduce which states that the accumulator should be an associative, non-interfering, stateless function for incorporating an additional element into a result. In other words since the identity is mutable the result will be broken in case of parallel execution.
As pointed in the comments below a correct option is using the reduction as follows:
return chars.stream().collect(
StringBuilder::new,
StringBuilder::append,
StringBuilder::append).toString();
The supplier StringBuilder::new will be used to create reusable containers which will be later combined.
The method you are looking for is java.util.Stream.reduce, particularly the overload with three parameters, identity, accumulator, and binary function. That is the correct equivalent to Scala's foldLeft.
However, you are not allowed to use Java's reduce that way, and also not Scala's foldLeft for that matter. Use collect instead.
It can be done by using Collectors:
public static <A, B> Collector<A, ?, B> foldLeft(final B init, final BiFunction<? super B, ? super A, ? extends B> f) {
return Collectors.collectingAndThen(
Collectors.reducing(Function.<B>identity(), a -> b -> f.apply(b, a), Function::andThen),
endo -> endo.apply(init)
);
}
Usage example:
IntStream.rangeClosed(1, 100).boxed().collect(foldLeft(50, (a, b) -> a - b)); // Output = -5000
For your question, this does what you wanted:
public String concatenate(List<Character> chars) {
return chars.stream()
.collect(foldLeft(new StringBuilder(), StringBuilder::append)).toString();
}
Others are correct there's no equivalent though. Here's a util that comes close-
<U, T> U foldLeft(Collection<T> sequence, U identity, BiFunction<U, ? super T, U> accumulator) {
U result = identity;
for (T element : sequence)
result = accumulator.apply(result, element);
return result;
}
your case using the above method would look like-
public String concatenate(List<Character> chars) {
return foldLeft(chars, new StringBuilder(""), StringBuilder::append).toString();
}
Or without the lambda method ref sugar,
public String concatenate(List<Character> chars) {
return foldLeft(chars, new StringBuilder(""), (stringBuilder, character) -> stringBuilder.append(character)).toString();
}

Categories

Resources