//: generics/Functional.java // ©2015 MindView LLC: see Copyright.txt import java.math.*; import java.util.concurrent.atomic.*; import java.util.*; import static net.mindview.util.Print.*; // Different types of function objects: interface Combiner { T combine(T x, T y); } interface UnaryFunction { R function(T x); } interface Collector extends UnaryFunction { T result(); // Extract result of collecting parameter } interface UnaryPredicate { boolean test(T x); } public class Functional { // Calls the Combiner object on each element to combine // it with a running result, which is finally returned: public static T reduce(Iterable seq, Combiner combiner) { Iterator it = seq.iterator(); if(it.hasNext()) { T result = it.next(); while(it.hasNext()) result = combiner.combine(result, it.next()); return result; } // If seq is the empty list: return null; // Or throw exception } // Take a function object and call it on each object in // the list, ignoring the return value. The function // object can act as a collecting parameter, so it is // returned at the end. public static Collector forEach(Iterable seq, Collector func) { for(T t : seq) func.function(t); return func; } // Creates a list of results by calling a // function object for each object in the list: public static List transform(Iterable seq, UnaryFunction func) { List result = new ArrayList<>(); for(T t : seq) result.add(func.function(t)); return result; } // Applies a unary predicate to each item in a sequence, // and returns a list of items that produced "true": public static List filter(Iterable seq, UnaryPredicate pred) { List result = new ArrayList<>(); for(T t : seq) if(pred.test(t)) result.add(t); return result; } // To use the above generic methods, we create // function objects to adapt to our particular needs: static class IntegerAdder implements Combiner { @Override public Integer combine(Integer x, Integer y) { return x + y; } } static class IntegerSubtracter implements Combiner { @Override public Integer combine(Integer x, Integer y) { return x - y; } } static class BigDecimalAdder implements Combiner { @Override public BigDecimal combine(BigDecimal x, BigDecimal y) { return x.add(y); } } static class BigIntegerAdder implements Combiner { @Override public BigInteger combine(BigInteger x, BigInteger y) { return x.add(y); } } static class AtomicLongAdder implements Combiner { @Override public AtomicLong combine(AtomicLong x, AtomicLong y) { // Not clear whether this is meaningful: return new AtomicLong(x.addAndGet(y.get())); } } // We can even make a UnaryFunction with an "ulp" // (Units in the last place): static class BigDecimalUlp implements UnaryFunction { @Override public BigDecimal function(BigDecimal x) { return x.ulp(); } } static class GreaterThan> implements UnaryPredicate { private T bound; public GreaterThan(T bound) { this.bound = bound; } @Override public boolean test(T x) { return x.compareTo(bound) > 0; } } static class MultiplyingIntegerCollector implements Collector { private Integer val = 1; @Override public Integer function(Integer x) { val *= x; return val; } @Override public Integer result() { return val; } } public static void main(String[] args) { // Generics, varargs & boxing working together: List li = Arrays.asList(1, 2, 3, 4, 5, 6, 7); Integer result = reduce(li, new IntegerAdder()); print(result); result = reduce(li, new IntegerSubtracter()); print(result); print(filter(li, new GreaterThan<>(4))); print(forEach(li, new MultiplyingIntegerCollector()).result()); print(forEach(filter(li, new GreaterThan<>(4)), new MultiplyingIntegerCollector()).result()); MathContext mc = new MathContext(7); List lbd = Arrays.asList( new BigDecimal(1.1, mc), new BigDecimal(2.2, mc), new BigDecimal(3.3, mc), new BigDecimal(4.4, mc)); BigDecimal rbd = reduce(lbd, new BigDecimalAdder()); print(rbd); print(filter(lbd, new GreaterThan<>(new BigDecimal(3)))); // Use the prime-generation facility of BigInteger: List lbi = new ArrayList<>(); BigInteger bi = BigInteger.valueOf(11); for(int i = 0; i < 11; i++) { lbi.add(bi); bi = bi.nextProbablePrime(); } print(lbi); BigInteger rbi = reduce(lbi, new BigIntegerAdder()); print(rbi); // The sum of this list of primes is also prime: print(rbi.isProbablePrime(5)); List lal = Arrays.asList( new AtomicLong(11), new AtomicLong(47), new AtomicLong(74), new AtomicLong(133)); AtomicLong ral = reduce(lal, new AtomicLongAdder()); print(ral); print(transform(lbd,new BigDecimalUlp())); } } /* Output: 28 -26 [5, 6, 7] 5040 210 11.000000 [3.300000, 4.400000] [11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47] 311 true 265 [0.000001, 0.000001, 0.000001, 0.000001] *///:~