Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Remember last value returned from a JDK 8 lambda

I threw together a quick Java implementation of a Taylor series expansion for the exponential function, because it was easy and fun:

package math.series;

import java.util.stream.IntStream;

/**
 * Created by Michael
 * Creation date 3/6/2016.
 * @link https://stackoverflow.com/questions/35826081/calculating-ex-in-c-sharp
 * @link https://en.wikipedia.org/wiki/Leibniz_formula_for_%CF%80
 */
public class TaylorSeries {


    public static final int DEFAULT_NUM_TERMS = 10;

    public static void main(String[] args) {
        int n = (args.length > 0) ? Integer.parseInt(args[0]) : DEFAULT_NUM_TERMS;
        System.out.println("pi");
        System.out.println(String.format("%10s %10s %10s %10s", "n", "series", "expected", "error"));
        double expected = Math.PI;
        double series = TaylorSeries.pi(0.0, n);
        double error = expected - series;
        System.out.println(String.format("%10d %10.6f %10.6f %10.6f", n, series, expected, error));

        System.out.println("exp");
        System.out.println(String.format("%10s %10s %10s %10s", "x", "series", "expected", "error"));
        for (double x = 0.0; x <= 3.0; x += 0.25) {
            expected = Math.exp(x);
            series = TaylorSeries.exp(x, n);
            error = expected - series;
            System.out.println(String.format("%10.6f %10.6f %10.6f %10.6f", x, series, expected, error));
        }
    }

    public static double exp(double x, int n) {
        double sum = 1.0;
        double term = 1.0;
        for (int i = 1; i <= n; ++i) {
            term *= x / i;
            sum += term;
        }
        return sum;
    }

    public static double pi(double x, int n) {
        return IntStream.range(0, n)
                .mapToDouble(i -> 8.0/(4*i+1)/(4*i+3))
                .sum();
    }
}

I'm ashamed to admit that my employer is still using JDK 6 and JDK 7; I'm not writing on JDK 8 during my work day yet. I have not groked all new features in the JDK, including lambdas.

I warmed up by writing a Taylor series expansion for pi using lambda. It's easy and elegant. Surprisingly, it requires a million terms to converge to six digits of accuracy, but that's the nature of the series.

I decided to try and implement the exponential function using a lambda. I don't want to do the naive thing and use the Math.pow or factorial functions; the implementation I posted without lambdas accomplishes both nicely.

I can't see how to have each step in the lambda remember what the value of the previous term was. Can anyone help a lambda beginner and give an example?

like image 689
duffymo Avatar asked Mar 06 '16 17:03

duffymo


People also ask

How do you use the final variable in lambda?

The local variables that a lambda expression may use are referred to as “effectively final”. An effectively final variable is one whose value doesn't change after it's first assigned. There is no need to explicitly declare such a variable as final, although doing so would not be an error.

What is the return type of lambda expression in Java 8?

The return type of a method in which lambda expression used in a return statement must be a functional interface.

Can lambda return string?

The lambda function assigned to full_name takes two arguments and returns a string interpolating the two parameters first and last .

What does -> mean in Java?

To support lambdas, Java has introduced a new operator “->”, also known as lambda operator or arrow operator. This arrow operator is required because we need to syntactically separate the parameter from the body. LambdaBody can be an expression or a block.


2 Answers

A possible solution is to implement a stateful function for returning the next term in the sequence:

public static double exp(double x, int n) {
    return DoubleStream.iterate(1, new DoubleUnaryOperator() {

        private int i = 1;

        @Override
        public double applyAsDouble(double operand) {
            return operand * x / i++;
        }

    }).limit(n).sum();
}

This will create a DoubleStream with the help of the iterate(seed, f) method where the seed is 1 and the function, which returns the next value, simply increments the current iteration number i and multiplies the previous value with x / i. The Stream is limited to n element with limit and the sum is retrieved with sum().

Sample calling code:

public static void main(String[] args) {
    System.out.println(exp(3, 500)); // prints "20.085536923187668"
}

with a result very close to the real one.

like image 173
Tunaki Avatar answered Sep 28 '22 14:09

Tunaki


Added a small improvement to Tunaki's solution: replaced the DoubleUnaryOperator anonymous class with a lambda and the i attribute with an AtomicInteger instance:

public static double exp(double x, int n) {
    final AtomicInteger integer = new AtomicInteger(1);
    return DoubleStream.iterate(
       1.0, 
       operand -> operand * x / integer.getAndIncrement()
    ).limit(n).sum();
}
like image 25
Adam Siemion Avatar answered Sep 28 '22 15:09

Adam Siemion