Home > Back-end >  Convert iterative method to functional with Java 8 Streams
Convert iterative method to functional with Java 8 Streams

Time:10-11

I have this algorithm right here,

    while ((start   end) / 2 != 0) {
        Double mid = (start   end) / 2;
        if (Math2.pow(mid, 3) < n) {
            start = mid;
        } else if (Math.abs(n - Math2.pow(mid, 3)) > Math2.pow(e, 3)) {
            end = mid;
        } else if (Math.abs(n - Math2.pow(mid, 3)) < Math2.pow(e, 3)) {
            return mid;
        }
    }
    return 0.0;

}

that gives me the cubic root of n with a loss precision of e. I need to do the same thing but using Java 8 Streams. Math2 is from a private git rep. You can use Math.pow instead; it will work too. How can I do the same algorithm with Streams?

CodePudding user response:

Java Stream API has method Stream::iterate starting from Java 9, therefore a class representing the iteration steps/states may be implemented as follows:

class CubeSolver {
    static final double EPS = 1E-06;
        
    private double start, end, n, mid;

    public CubeSolver(double s, double e, double n) {
        this.start = s;
        this.end = e;
        this.n = n;
        this.mid = (start   end) / 2;
    }

    // UnaryOperator<CubeSolver> for iteration
    public CubeSolver next() {
        if (done()) {
            return this;
        }
        if (Math.pow(mid, 3) < n) {
            start = mid;
        } else if (Math.abs(n - Math.pow(mid, 3)) > EPS) {
            end = mid;
        }
        return new CubeSolver(start, end, n);
    }
    
    // define end of calculation
    public boolean done() {
        return mid == 0 || Math.abs(n - Math.pow(mid, 3)) < EPS;
    }
    
    @Override
    public String toString() {
        return "root = "   mid;
    }
}

Then the stream-based solution looks like this:

  1. define an initial seed with start, end, n
  2. use Stream::iterate with hasNext predicate to create a finite stream 2a) or use older Stream::iterate without hasNext but with Stream::takeWhile operation to conditionally limit the stream - also available since Java 9
  3. use Stream::reduce to get the last element of the stream
CubeSolver seed = new CubeSolver(1.8, 2.8, 8);
CubeSolver solution = Stream
    .iterate(seed, cs -> !cs.done(), CubeSolver::next)
    .reduce((first, last) -> last) // Optional<CubeSolver>
    .orElse(null);
    
System.out.println(solution); 

Output:

root = 2.0000002861022947

In Java 11 static Predicate::not was added, so the 2a solution using takeWhile could look like this:

CubeSolver seed = new CubeSolver(0, 7, 125);
CubeSolver solution = Stream
    .iterate(seed, CubeSolver::next)
    .takeWhile(Predicate.not(CubeSolver::done))
    .reduce((first, last) -> last) // Optional<CubeSolver>
    .orElse(null);
    
System.out.println(solution); 

Output (for EPS = 1E-12):

root = 4.999999999999957
  • Related