Functional Java Lambda Expressions 1
Functions as a First Class Citizen If there was just some way of packaging the compare function And then passing that function as an argument to sort Then we wouldn t need an object We wouldn t need a Comparitor interface We wouldn t need an anonymous inner class or an explicit class We wouldn t need to pass data to the sort method But how can we package a function? 2
Lambda Expressions Invented by Alonzo Church in the 1930 s Method to express an anonymous function Supported in Java 1.8 Simplest form: x -> x*x Parameter name comes first Then -> to indicate this is a lambda expression Then an expression to evaluate the result Can have multiple parameters: (x,y)->x*y Can have multiple statements in braces {} with return 3
History of Lambda Expressions 1956 Information Processing Langauge Allen Newell, Cliff Shaw, and Herbert Siman at RAND/Carnegie IT List processing (dynamic memory, types, recursion, multi-tasking) 1958 LISP John McCarthy at MIT (IBM summer) 2nd major language (after FORTRAN) Mixes data and functions 1970 SCHEME LISP dialect using lambdas Guy Steele & Gerald Sussman at MIT 4
Lambda Concept Provide a way to write a function in Java That does not require a class That does not require a method That can be encapsulated and passed around like data That is not executed right away but can be executed when we are ready Lambda expressions are a way of writing a function Specify parameters Specify a return value 5
Lambda Expression Syntax (argument_list) -> return_expression; argument_list : a comma separated list of variable names Types unspecified! Types are determined when the lambda expression is used return_expression : Any java expression Can use variable(s) from the argument list Expression value is implicitly returned 6
Lambda Expression Extended Syntax (argument_list) ->{ lambda_statements }; argument_list : a comma separated list of variable names Types unspecified! Types are determined when the lambda expression is used lambda_statements : Any java list of statements Can use variable(s) from the argument list Explicit return required 7
How are Lambda Expressions Used? May be used in place of Functional Interfaces interfaces which specify a single method For example Runnable, ActionListener, Comparable See java.util.function in the Java library for generic functional interfaces Of course, we can write our own functional interfaces as well Instead of specifying an object which implements the interface, we can (finally) just specify a lambda expression! 8
Simple Lambda Expression Example public class TestRun { public static void main(string[] args) { Runnable runref = ( ) -> System.out.println("Running runrefs run method"); runref.run( ); } } Running runrefs run method 9
Lambda Expression Comparator Arrays.sort(circles, (arg0, arg1) -> { int retval=0; double area0 = arg0.width*arg0.height; double area1 = arg1.width*arg1.height; if(area0 < area1) retval = -1; else if(area0 > area1) retval = 1; return retval; } ); 10
Simplifying further Why not use the same trick as we used comparing integers? (arg0, arg1) -> arg0.width*arg0.height - arg1.width*arg1.height Problem: Comparators need to return integers 11
Functionalizing Comparisons What if there were a function that maps an object to a number For instance ellipse -> area Reduce comparing objects to comparing numbers The Comparable interface includes static helper methods that use this trick e.g. comparingdouble comparingdouble argument : object which implements the ToDoubleFunction interface ToDoubleFunction interface defines a single method: double applyasdouble(t value) where T is a generic type ToDoubleFunction is a functional interface and may be replaced by a lambda expression! 12
Simplify with comparingdouble Anonymous inner Comparator class: private Comparator<Ellipse2D.Double> comp1 = Comparator.comparingDouble(e-> e.width*e.height); Arrays.sort(circles, comp1); In fact the Comparator object may as well be anonymous: Arrays.sort(circles, Comparator.comparingDouble(e -> e.width*e.height)); 13
Typical functional construct : map map is a function which takes two arguments A function A vector Map applies the function to each element of the vector and computes multiple results The return value of map is a vector of results map *2 [1, 2, 3, 4, 5] [2, 4, 6, 8, 10] 14
Lambda in Other Languages: Scheme Scheme (1975) is a Lisp (1958) dialect > (define (square x) (* x x )) > (map square '(1 2 3 4)) (list 1 4 9 16) Anonymous lambda expression version > (map (lambda (x) (* x x)) '(1 2 3 4)) (list 1 4 9 16) Implementation of Scheme @ http://download.racket-lang.org/ 15
Lambda in other languages: Haskell Haskell (1990) Most popular functional language Put this line in 'Test.hs : square x = x * x Load it with (:l is the load command, l is ell) : :l Test Run: map square [1, 2, 3, 4] [1, 4, 9, 16] or using a lambda expression: map (\x -> x* x) [1, 2, 3, 4] [1, 4, 9, 16] 16
Lambda in Other Languages: Python(3) >>> a= [1,2,3,4] >>> sq = lambda x: x*x >>> list(map(sq,a)) [1, 4, 9, 16] >>> list(map(lambda x:x*x, a)) [1, 4, 9, 16] >>> def squ (n):... return n*n... >>> list(map(squ,a)) [1, 4, 9, 16] 17
Functions as Arguments: C int square ( int x ) { return x * x; } int* map ( int (*f)(int), int len, int array[ ]) { int i = 0; int* ret = (int*)malloc(len*sizeof(int)); for(i = 0; i < len; i++) { ret[i] = (*f)(array[i]); } return ret; } void main () { int arg[ ] = {1, 2, 3, 4}; int* tmp = map(square, 4, arg); printf("[%d, %d, %d, %d]\n", tmp[0], tmp[1], tmp[2], tmp[3]); } 18
Defining map as static method in Java import java.util.function.function; // Java 1.8 Interface: apply method public class Mapper { public static double[ ] map(double[ ] array, Function<Double, Double> fn) { double[ ] temp = null; if(array!= null) { temp = new double[array.length]; for(int i = 0; i < array.length; i++) { temp[i] = fn.apply(array[i]); } } return temp; } } 19
Defining Function to be Passed In an anonymous inner class: static Function<Double, Double> square = new Function<Double, Double>() { @Override public Double apply(double t) { return t*t; } }; As a stand-alone method public static double sqr(double d) { return d*d; } 20
Passing Functions in to Functions public static void main(string[ ] args){ double[] data = {1,2,3,4}; double[] sq1 = Mapper.map(data, square); double[] sq2 = map(data, Mapper::sqr); // Note :: double[] sq3 = map(data, Math::sqrt); // sqrt in Java lib double[] sq4 = map(data, d -> d*d); // lambda } 21
Defining map as a dynamic method import java.util.function.function; // Java 1.8 Interface: apply method public class MapDyn { public double[ ] map(double[ ] array, Function<Double, Double> fn) { double[ ] temp = null; if(array!= null) { temp = new double[array.length]; for(int i = 0; i < array.length; i++) { temp[i] = fn.apply(array[i]); } } } return temp; this is available, but not used 22
Defining Function to be Passed (dyn) In an anonymous inner class: Function<Double, Double> square = this is available, but not used new Function<Double, Double>() { @Override public Double apply(double t) { return t*t; } }; As a stand-alone method public static double sqr(double d) { return d*d; } 23
Passing Functions (dynamic) public static void main(string[ ] args){ need a reference double[] data = {1,2,3,4}; variable to find MapDyn test = new MapDyn(); the map method double[] sq1 = test.map(data, test.square); double[] sq2 = test.map(data, MapDyn::sqr); // Note :: double[] sq3 = test.map(data, Math::sqrt); double[] sq4 = test.map(data, d -> d*d); // lambda } 24