name: inverse layout: true class: taylor msoe --- class: center, middle .title[ # Functional Programming ] ??? Toggle speaker view by pressing P Pressing C clones the slideshow view, which is the view to put on the projector if you're using speaker view. Press ? to toggle keyboard commands help. --- # OO versus Functional * Object-oriented: **group** data with operations -- * Functional: treat operations **as** data ??? ## OO * modular and encapsulated * Code reuse through inheritance and composition * Functionality and data tightly coupled ## Functional * Can define functionality and pass it as a parameter * Well suited for concurrent and event driven --- class: animated slideInUp center, middle # Java 8 ## Added many functional features --- # Functional Characteristics * Function call can be replace by the result -- * Functions are not allowed to have side effects -- * Compiler is allowed to rearrange function invocations -- ... or run on different threads -- * Program flow driven by data dependencies, not order of instructions ??? * Data must be immutable * If changed, new copy is created --- # First-Class Functions Functions can be * Assigned to variables -- * Passed as arguments to other functions -- * Returned by other functions --- class: animated fadeIn # Lambda Functions ``` Button button = new Button("Run"); button.setOnAction(`event -> run(event)`); ``` -- * `run(event)` is a function that is passed to a method * Under the hood it is really the implementation of an interface -- ``` Button button = new Button("Run"); button.setOnAction(`new EventHandler
() {` `@Override` `private void handle(ActionEvent event) {` ` run(event);` `}}`); ``` --- # Functional Interface ``` @FunctionalInterface public interface EventHandler
{ void handle(T event); } ``` * An interface with only one method -- * Annotated as a `@FunctionalInterface` -- * Can use lambda expression --- # `Collection` and `Stream` Interfaces * `Collection` - Efficient management of and access to their elements -- * `Stream` - A sequence of elements supporting sequential and parallel aggregate operations -- - A declarative description of their source and computational operations which will be performed in aggregate on that source --- # `Stream` Methods * `count()` - returns the number of elements in the stream * `distinct()` - returns a stream with duplicates removed * `limit(long maxSize)` - returns a stream with no more than `maxSize` elements * `skip(long count)` - returns a stream without the first `count` elements * `sorted()` - returns a stream in sorted order --- # Example - `count()` and `distinct()` ``` List
words = Arrays.asList("a", "function", "that", "returns", "a", "function"); long count = words.stream() .count(); long numberUnique = words.stream() .distinct() .count(); System.out.println("Count: " + count + " Unique: " + numberUnique); ``` -- #### Output: ```diff Count: 6 Unique: 4 ``` --- # Higher-Order Functions * Accept other functions as argument(s) * Return a function as a result -- * E.g., `setOnAction(EventHandler
e)` --- # Higher-Order Functions in `Stream` * `allMatch(Predicate super T> predicate)` * `anyMatch(Predicate super T> predicate)` * `noneMatch(Predicate super T> predicate)` * `filter(Predicate super T> predicate)` --- # `Predicate` Interface ``` @FunctionalInterface public interface Predicate
{ boolean test(T t); } ``` .right[$ ^* $Not showing `default` methods] --- # Higher-Order Functions in `Stream` ... again * `allMatch(Predicate super T> predicate)` * `anyMatch(Predicate super T> predicate)` * `noneMatch(Predicate super T> predicate)` * `filter(Predicate super T> predicate)` --- # Example - `filter()` ``` List
words = Arrays.asList("a", "function", "that", "returns", "a", "function"); Predicate
isShort = (word -> word!=null && word.length()<5); Object[] shortWords = words.stream() .filter(isShort) .toArray(); for(Object word : shortWords) { System.out.print(word + " "); } System.out.println(); ``` -- #### Output: ```diff a that a ``` --- # More Higher-Order Functions in `Stream` * `map(Function super T, ? extends R> mapper)` * `mapToDouble(Function super T, ? extends R> mapper)` * `mapToInt(Function super T, ? extends R> mapper)` --- # `Function` Interface ``` @FunctionalInterface public interface Function
{ R apply(T t); } ``` .right[$ ^* $Not showing `default` methods] --- # Example - `map()` ``` List
words = Arrays.asList("a", "function", "that", "returns", "a", "function"); List
lengths = words.stream() .map(word -> word + ": " + word.length()) .collect(Collectors.toList()); System.out.println(lengths); ``` -- #### Alternative: ``` Function
toLength = (word -> word + ": " + word.length()); words.stream().map(toLength); ``` -- #### Output: ```diff [a: 1, function: 8, that: 4, returns: 7, a: 1, function: 8] ``` --- # Numeric Streams .center[`IntStream`, `DoubleStream`, and `LongStream`] * `sum()` * `max()` * `min()` * `average()` * `summaryStatistics()` * `reduce()` --- # Example - `mapToInt()` ``` int sum = words.stream() .mapToInt(w -> w.length()); .sum(); ``` -- #### Output: ```diff Sum of lengths of all strings: 29 ``` --- # `Consumer` Interface ``` @FunctionalInterface public interface Consumer
{ void accept(T t); } ``` .right[$ ^* $Not showing `default` methods] --- # Example - `forEach()` ``` List
words = Arrays.asList("a", "function", "that", "returns", "a", "function"); words.stream() .forEach(w -> System.out.println(w + ": " + w.length())); ``` -- #### Output: ```diff a: 1 function: 8 that: 4 returns: 7 a: 1 function: 8 ``` Note: we can do `forEach()` directly on a `List`: ``` words.forEach(w -> System.out.println(w + ": " + w.length())); ``` --- # Extract Even Numbers ``` Stream
numbers = Stream.of(3, 8, 7, 2, 1, 9, 8); List
evens = numbers.filter(i -> i%2==0) .collect(Collectors.toList()); System.out.println(evens); ``` -- #### Output: ``` [8, 2, 8] ``` --- # Extract Based on Suffix ``` System.out.println(Stream.of("happy", "discussion" /* ... */, "locomotion") .filter(word -> word.endsWith("tion")) .count()); ``` -- #### Output: ``` 1 ``` --- # Extract Circles ``` Stream
shapes = Stream.of(new Circle(0, 0), new Square(0, 0, 4), new Circle(0, 0), new Triangle(0, 0, 1, 5)); List
circles = shapes.filter(shape -> shape instanceof Circle) .map(shape -> (Circle)shape) .collect(Collectors.toList()); ``` --- # Sum ``` double totalArea = shapes.mapToDouble(shape -> shape.getArea()) .sum(); System.out.println("Total area: " + totalArea); ``` -- #### Output: ``` Total area: 38.239208802178716 ``` --- # Average ``` OptionalDouble averageArea = shapes.mapToDouble(Shape::getArea) .average(); if(averageArea.isPresent()) { System.out.println("Average area: " + averageArea.getAsDouble()); } ``` -- #### Output: ``` Average area: 9.559802200544679 ``` --- # Summary Statistics ``` DoubleSummaryStatistics areaStats = shapes.mapToDouble(Shape::getArea) .summaryStatistics(); System.out.println("Number of shapes: " + areaStats.getCount()); System.out.println("Total area: " + areaStats.getSum()); System.out.println("Average area: " + areaStats.getAverage()); System.out.println("Maximum area: " + areaStats.getMax()); System.out.println("Minimum area: " + areaStats.getMin()); ``` -- #### Output: ``` Number of shapes: 4 Total area: 38.239208802178716 Average area: 9.559802200544679 Maximum area: 16.0 Minimum area: 2.5 ``` --- # Average with `reduce()` ``` double area = shapes.map(Shape::getArea) .reduce(0.0, (a, b) -> a+b); System.out.println("Total area: " + area); ``` -- #### Output: ``` Total area: 38.239208802178716 ```