0% encontró este documento útil (0 votos)
99 vistas15 páginas

Modulo 1

Este documento presenta conceptos sobre programación funcional y Stream en Java 11. Cubre temas como métodos en POO, funciones en álgebra, programación funcional, imperativa y declarativa, así como principios funcionales como funciones puras, composición, currying, evitar estados compartidos y mutabilidad, y evitar efectos secundarios. También introduce conceptos de mónadas y cómo flatMap ayuda a evitar problemas al acceder a objetos opcionales.
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
99 vistas15 páginas

Modulo 1

Este documento presenta conceptos sobre programación funcional y Stream en Java 11. Cubre temas como métodos en POO, funciones en álgebra, programación funcional, imperativa y declarativa, así como principios funcionales como funciones puras, composición, currying, evitar estados compartidos y mutabilidad, y evitar efectos secundarios. También introduce conceptos de mónadas y cómo flatMap ayuda a evitar problemas al acceder a objetos opcionales.
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
Está en la página 1/ 15

Ciclo de formación - Módulo 1

● Programación funcional y Stream en JAVA 11


● Raul A. Alzate ​[email protected]

Conceptos básicos
¿Qué es un método en POO?
Car car = ​new​ Car()

Un método es un procedimiento asociado con una clase.

car.run()

Un método define el comportamiento de los objetos que se crean a partir de la clase. Un método
es una acción que un objeto puede realizar.

car.addSpeed(​2​)

¿Qué es una función en álgebra?


<center>

cal(𝒙) = 2𝒙

</center>

Lo que significa que estamos declarando una función llamada cal y toma un argumento llamado 𝒙
y multiplica 𝒙 por 2.

Para usar esta función, simplemente proporcionamos un valor para 𝒙:

<center>

cal(2)

</center>

En álgebra, esto significa exactamente lo mismo que escribir: ​4

¿Qué es la programación funcional?


La programación funcional es un paradigma de programación que nos permite diseñar funciones
de forma independiente de la logica programada. Cada funcion cumple una funcion definida y
puede ser extendida y compuesta por otroas funciones.

La programación funcional tiene la ventaja de ser razonada matemáticamente, permitiendo el


uso de mecanismos matemáticos para optimizar el rendimiento del programa.

Son confiables, elegantes y expresivos.


Veamos los principios para aplicar este paradigma:

● Las funciones debe ser puras.


● Deben permitir la composición entre otras función.
● Debe evitar estado compartido.
● Es necesario evitar el estado mutante.
● No debe generar efectos secundarios.

Paradigma descriptivo
En la ​programación imperativa​ se describe paso a paso un conjunto de instrucciones que
deben ejecutarse para variar el estado del programa y hallar la solución, es decir, un algoritmo en
el que se describen los pasos necesarios para solucionar el problema.

En la programación declarativa las sentencias que se utilizan lo que hacen es describir el


problema que se quiere solucionar; se programa diciendo lo que se quiere resolver a nivel de
usuario, pero no las instrucciones necesarias para solucionarlo. Esto último se realizará mediante
mecanismos internos de inferencia de información a partir de la descripción realizada.

Programación imperativa
Es un lenguaje formal que proporciona una serie de instrucciones que permiten a un
programador escribir secuencias de órdenes y algoritmos a modo de controlar el comportamiento
físico y lógico de una computadora con el objetivo de que produzca diversas clases de datos

List<Integer> ​intersection​(List<Integer> a, List<Integer> b) {


List<Integer> result = ​new​ ArrayList<>();
​for​ (var i = ​0​; i < a.size(); i++) {
​for​ (var j = ​0​; j < b.size(); j++) {
​if​ (a.get(i).equals(b.get(j))) {
result.add(a.get(i));
​break​;
}
}
}
Collections.sort(result);
​return​ result;
}

intersection(List.of(​1​,​7​,​3​), List.of(​4​,​5​,​6​));
[]

Listo, una solución clara, que podemos leer de arriba a abajo y de izquierda a derecha, en el más
puro estilo imperativo. Podríamos decir que, desde el punto de vista de java, nuestro código tiene
un nivel bajo de abstracción, y de hecho la única abstracción sería el uso de add(), get() y el sort()
del final, por supuesto.

import​ java.util.Arrays;
import​ java.util.List;
import​ java.util.function.BiFunction;
import​ java.util.stream.Collectors;

BiFunction<List<Integer>, List<Integer>, List<Integer>>


intersection =(a, b) -> a.stream()
.filter(value -> b.indexOf(value) > -​1​)
.collect(Collectors.toList());

List<Integer> a = Arrays.asList(​2​, 4
​ ​, 5​ ​);
List<Integer> b = Arrays.asList(​5​, 4​ ​, 1​ ​);

intersection.apply(a,b)

[4, 5]

Principios y fundamentos
Función pura
Una función pura es que:

● Dada la misma entrada, siempre devolverá la misma salida.


● No produce efectos secundarios.
● No se basa en un estado mutable externo.

Las funciones puras también son extremadamente independientes: fáciles de mover,


refactorizar y reorganizar en su código, haciendo que sus programas sean más flexibles y
adaptables a los cambios futuros.
public​ ​static​ ​int​ ​squareSum​(​final​ List<Integer> values) {
​int​ total = ​0​;
​for​(​int​ e : values) {
total += e * ​2​; ​// mutates a local variable
}
​return​ total;
}
//2*2 + 4*2 + 5*2
squareSum(Arrays.asList(​2​, ​4​, ​5​))

22

Applicando funciones pura

//2*2 + 4*2 + 5*2


Arrays.asList(​2​, ​4​, ​5​).stream().map(e-> e*​2​).reduce(​0​, (c, e)-> c + e)

22

Arrays.asList(​2​, ​4​, ​5​).stream()


.filter(e -> e%​2​ == ​0​)
.map(e-> e*​2​)
.reduce(​0​, (c, e)-> c + e)

12

Función de composición
La composición de funciones es el proceso de combinar dos o más funciones para producir una
nueva función o realizar algún cálculo. Por ejemplo, la composición f. g (el punto significa
“compuesto con”) es equivalente a f (g (x)).

Todo tiene que ver con la creación de pequeñas funciones reutilizables que puede combinar para
componer nuevas funciones.

import​ java.util.function.Function;

Function<Integer,Integer> add = (a) -> a + ​3​;


Function<Integer,Integer> times = (a) -> a * 2​ ​ ;

Function<Integer,Integer> composedA = add.compose(times) ;


Function<Integer,Integer> composedB = times.compose(add) ;

//composedB.apply(3);
composedA.apply(​3​);

Currier es…
BiFunction<Integer,Integer,Integer> adder = (a, b) -> a + b ;
Function<Integer,Function<Integer,Integer>> currier = a -> b ->
adder.apply( a, b ) ;

Function<Integer,Integer> curried = currier.apply(​4​);

​// ( 4 + 3 )
System.out.printf(​"Curry : %d"​, curried.apply( ​3​ ));

Curry : 7

java.io.PrintStream@1da806e2

Evitar los estados compartidos


El estado compartido es cualquier variable, objeto o espacio de memoria que existe en un ámbito
compartido, o como la propiedad de un objeto que se pasa entre ámbitos. Un ámbito compartido
puede incluir un ámbito global o ámbitos de cierre. A menudo, en la programación orientada a
objetos, los objetos se comparten entre ámbitos agregando propiedades a otros objetos.

class​ ​DriveCar​ {
​public​ ​void​ ​run​(){
​this​.speed = ​10​;
}
​public​ ​void​ ​addSpeed​(Speed v){
​this​.speed = v.advance(​this​.speed++);
}

​public​ ​static​ ​class​ ​Speed​{


​public​ ​int​ ​advance​(​int​ speed){
​return​ speed*​10​;
}
}
}

● Cuando evita el estado compartido, el tiempo y el orden de las llamadas a las funciones no
cambian el resultado de llamar a la función. Con funciones puras, con la misma entrada,
siempre obtendrá la misma salida.
● La programación funcional establece que las funciones deben evitar estados mutables.
Eso significa que no deben asignar ni actualizar ningún estado de un objeto.
● Las funciones son independientes en sus ejecuciones y aseguran que no haya efectos
secundarios durante su ejecución.

Function<Integer, Integer> x1 = val -> val + ​1​;


Function<Integer, Integer> x2 = val -> val * 2​ ​;

final​ ​int​ x = ​2​;


Integer val1 = x1.apply(x);
Integer val2 = x2.apply(val1);

System.out.println(val2);
x1.apply(val2);
x2.apply(x);

System.out.println(val2);

6
6

Evitar la mutabilidad de los estados


Necesitamos evitar las referencias de objetos, ya que podemos afectar el comportamiento del
objeto y tener una mala ejecución en el programa. Necesitamos crear otra referencia para ello.
Evite el estado mutante, utilizando diferentes técnicas y bibliotecas. Los objetos inmutables son
siempre valores.

Este es uno de los principios básicos de la programación funcional. Si paso un valor a una
función, puedo prometer que se mantendrá igual para siempre.

● Usando la palabra reservada final para variables y construcciones


● Usamos la clase Collections.unmodifiableXX
● Podemos usar la biblioteca ​https://immutables.github.io/

List<Integer> list = Arrays.asList(​2​, ​3​, ​4​);

List<Integer> list2 = list.stream().filter(e -> e % ​2​ == ​0​)


.collect(Collectors.collectingAndThen(
Collectors.toList(), Collections::unmodifiableList
));

list2.add(​2​);​//java.lang.UnsupportedOperationException
System.out.println(list2);

-----------------------------------------------------------------------
----

java.lang.UnsupportedOperationException: null

at
java.base/java.util.Collections$UnmodifiableCollection.add(Collections.
java:1060)

at .(#89:1)

Evitar efectos secundarios


● Modificar cualquier variable externa o propiedad de objeto (por ejemplo, una variable
global o una variable en la cadena de alcance de la función principal)
Iniciando sesión en la consola
● Escribiendo en la pantalla
● Escribir en un archivo
● Escribiendo a la red
● Disparando cualquier proceso externo
● Llamar a otras funciones con efectos secundarios

Una mónada es algo muy simple, pero extremadamente poderoso. Una mónada un conjunto de
tres cosas:

● Parameterized type: Optional<T>


● unit: Optional.of()
● bind: Optional.flatMap()

¿Qué es una monad?


Una mónada es algo muy simple, pero extremadamente poderoso. Una mónada un conjunto de
tres cosas:

● Un tipo de parametrización M<T>


● Un función “unit” T -> M<T>
● Un operador “bind” M<T> bind T -> M<U> = M<U>

¿Qué problemas puede pasar aquí?


Person person = personMap.get(​"Name"​);
process(person.getAddress().getCity());

En que nos ayuda el flat map


La diferencia es que ​map()​ devuelve el mismo número de elementos que el Stream de entrada ya
que es simplemente una proyección de los elementos de entrada. Es decir cada elemento de
entrada se transforma en un elemento de salida.

Por otro lado ​flatMap()​, proyecta una lista de elementos de cada elemento original y los
concatena en un único stream.

import​ java.util.HashMap;
import​ java.util.Optional;
import​ java.util.function.Consumer;
import​ java.util.function.Function;

public​ ​final​ ​class​ ​Main​ {

​private​ Consumer<City> process = city -> {


System.out.println(city);
};

​public​ ​static​ ​void​ m


​ ain​(String[] arg) {
Main example = n ​ ew​ Main();
Map<String, Person> personMap = ​new​ Map<>();
personMap.put(​"Name"​, ​new​ Person(​new​ Address(​new
City(​"Medellin"​))));

personMap.find(​"Name"​)
.flatMap(Person::getAddress)
.flatMap(Address::getCity)
.ifPresent(example.process);
}

​public​ ​static​ ​class​ ​Person​ {


​private​ ​final​ Address address;

​public​ ​Person​(Address address) {


​this​.address = address;
}

​public​ Optional<Address> ​getAddress​() {


​return​ Optional.of(address);
}

​public​ ​static​ ​class​ ​Address​ {


​private​ ​final​ City city;

​public​ ​Address​(City city) {


​this​.city = city;
}

​public​ Optional<City> ​getCity​() {


​return​ Optional.of(city);
}
}

​public​ ​static​ ​class​ ​City​ {


​private​ ​final​ String name;

​public​ ​City​(String name) {


​this​.name = name;
}

​public​ Optional<String> ​getName​() {


​return​ Optional.of(name);
}

@Override
​public​ String ​toString​(){
​return​ name;
}
}

​public​ ​static​ ​class​ ​Map​<​T​, ​U​> ​extends​ ​HashMap​<​T​, ​U​> {


​public​ Optional<U> ​find​(T key) {
​return​ Optional.ofNullable(​super​.get(key));
}
}

}
Main.main(​new​ String[]{});

Medellin

Más ejemplos sobre map y flatMap


List<Integer> numeros = ​new​ ArrayList<Integer>(Arrays.asList(​1​, ​2​, ​3​,
4​));

List<Integer> cuadrados = numeros.stream()


.map( x -> x * x)
.collect(Collectors.toList());

for​ (Integer n : cuadrados) {


System.out.println(n);
}

1
4
9
16

List<List<Integer>> listaBidimensional = ​new


ArrayList<List<Integer>>(Arrays.asList(
​new​ ArrayList<Integer>(Arrays.asList(​1​, ​2​)),
​new​ ArrayList<Integer>(Arrays.asList(​3​, ​4​))
));

List<Integer> listaAplanada = listaBidimensional.stream()


.flatMap( listaInterna -> listaInterna.stream())
.collect(Collectors.toList());

for​ (Integer n : listaAplanada) {


System.out.println(n);
}
1
2
3
4

Práctica usando Stream y Funciones


En esta práctica vamos a implementar una caso de uso donde podemos explorar los conceptos
vistos.

<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-csv</artifactId>
<version>2.10.3</version>
</dependency>

Caso de uso
Se tiene un archivo CSV donde se tiene unos jugadores de Fútbol de varias nacionalidades y
diferentes cluds. Se tiene un modelo para realizar el mapeo y posteriormente integrar estos
modelos, donde le podemos aplicar filtros y demás funciones completas de Java.

Veamos el modelo siguiente:

import​ com.fasterxml.jackson.annotation.JsonPropertyOrder;

@JsonPropertyOrder({​"id"​,​"name"​,​"age"​,​"photo"​,​"nationality"​, ​"flag"​,
"overall"​, ​"potential"​, ​"club"​})
public​ ​class​ ​Player​ {

​private​ String id;


​private​ String name;
​private​ String age;
​private​ String photo;
​private​ String nationality;
​private​ String flag;
​private​ String overall;
​private​ String club;
​public​ String ​getId​() {​return​ id;}
​public​ ​void​ ​setId​(String id) {​this​.id = id;}
​public​ String ​getName​() {​return​ name;}
​public​ ​void​ ​setName​(String name) {​this​.name = name;}
​public​ String ​getAge​() {​return​ age;}
​public​ ​void​ ​setAge​(String age) {​this​.age = age;}
​public​ String ​getPhoto​() {​return​ photo;}
​public​ ​void​ ​setPhoto​(String photo) {​this​.photo = photo;}
​public​ String ​getNationality​() {​return​ nationality;}
​public​ ​void​ ​setNationality​(String nationality) {​this​.nationality =
nationality;}
​public​ String ​getFlag​() {​return​ flag;}
​public​ ​void​ ​setFlag​(String flag) {​this​.flag = flag;}
​public​ String ​getOverall​() {​return​ overall;}
​public​ ​void​ ​setOverall​(String overall) {​this​.overall = overall;}
​public​ String ​getClub​() {​return​ club;}
​public​ ​void​ ​setClub​(String club) {​this​.club = club;}

En esta clase nos soporta un método de utilidad para obtener los jugadores de un archivo
externo.

import​ com.fasterxml.jackson.databind.MappingIterator;
import​ com.fasterxml.jackson.dataformat.csv.CsvMapper;
import​ com.fasterxml.jackson.dataformat.csv.CsvParser;

import​ java.io.*;
import​ java.util.List;

public​ ​class​ ​Utils​ {


​public​ ​static​ List<Player> ​getPlayers​() {
File initialFile = ​new​ File(​"data.csv"​);
MappingIterator<Player> personIter;
​try​ (InputStream csvfile = ​new​ FileInputStream(initialFile)) {
personIter = ​new​ CsvMapper()

.enable(CsvParser.Feature.IGNORE_TRAILING_UNMAPPABLE)
.readerWithTypedSchemaFor(Player.class)
.readValues(csvfile);
​return​ personIter.readAll();
} ​catch​ (IOException e) {
​throw​ ​new​ RuntimeException(e.getMessage());
}
}
}

Obtener los jugadores con la máxima edad


Realizar una función ​List<Player> getPlayersWithMaxAge(List<Player> list, int
age);​ para obtener los jugadores con mayor edad.

import​ java.util.List;
import​ java.util.stream.Collectors;
public​ List<Player> ​getPlayersWithMaxAge​(List<Player> list, ​int​ age) {
​return​ list.stream()
.filter(player -> !player.getAge().isEmpty())
.filter(player -> {
var parseAge = Integer.parseInt(player.getAge());
​return​ parseAge > age;
}).collect(Collectors.toList());
}

Funciones de java util


Hacer uso de las siguientes funciones de Java como parte del ejemplo práctico:

● java.util.function.Consumer;
● java.util.function.Function;
● java.util.function.Supplier;
● java.util.function.Predicate;
● java.util.function.BiFunction;

import​ java.util.function.Consumer;
import​ java.util.function.Function;
import​ java.util.function.Supplier;
import​ java.util.function.Predicate;
import​ java.util.function.BiFunction;

Consumer<Object> print = (Object msn) -> {


System.out.println(msn);
}; ​//accept

Function<String, Integer> knowLength = (String str) -> {


​return​ str.length();
};​//apply

Supplier<String> world = () -> {


​return​ ​"Hola Mundo"​;
};​//get

Predicate<String> equal = (String a) -> {


​return​ a.length() >= ​5​;
};​//test

//print.accept(knowLength.apply(world.get()));

if​(equal.test(​"hola"​)){
print.accept(​"OK"​);
}

class​ ​Tupla​ {
​final​ Integer x;
​final​ Integer y;
Tupla(Integer x, Integer y){
​this​.x = x;
​this​.y = y;
}
}

BiFunction<Integer, Supplier<Integer>, Integer> add = (Integer a,


Supplier<Integer> b) -> {
​return​ a + b.get();
};​//apply

add.apply(​3​, () -> {
​return​ ​100​;
})

103

Aplicando mas mapas para transformar el objeto.


class​ ​FormatPlayer​ {
String name;
String club;

FormatPlayer(Player player){
​this​.name = player.getName();
​this​.club = player.getClub();
}

FormatPlayer(String name, String club){


​this​.name = name;
​this​.club = club;
}

​public​ String ​toString​(){


​return​ name+​" - "​+club;
}
}

List<Player> list = Utils.getPlayers();

var stream = list.stream()


.filter(a -> ​"FC Barcelona"​.equals(a.getClub()))
.map(FormatPlayer::​new​);

stream.sorted((o1, o2) -> o1.name.compareTo(o2.name))


.forEach(System.out::println);
//var result = stream.collect(Collectors.toList());

A. Vidal - FC Barcelona
Abel Ruiz - FC Barcelona
Aleñá - FC Barcelona
Arthur - FC Barcelona
C. Lenglet - FC Barcelona
Chumi - FC Barcelona
Coutinho - FC Barcelona
Denis Suárez - FC Barcelona
Ezkieta - FC Barcelona
Guillem Jaime - FC Barcelona
I. Rakitić - FC Barcelona
Iñaki Peña - FC Barcelona
J. Cillessen - FC Barcelona
Jordi Alba - FC Barcelona
Jorge Cuenca - FC Barcelona
L. Messi - FC Barcelona
L. Suárez - FC Barcelona
M. Wagué - FC Barcelona
M. ter Stegen - FC Barcelona
Malcom - FC Barcelona
Miranda - FC Barcelona
Munir - FC Barcelona
Nélson Semedo - FC Barcelona
O. Dembélé - FC Barcelona
Oriol Busquets - FC Barcelona
Piqué - FC Barcelona
Rafinha - FC Barcelona
Riqui Puig - FC Barcelona
S. Umtiti - FC Barcelona
Sergi Roberto - FC Barcelona
Sergi Samper - FC Barcelona
Sergio Busquets - FC Barcelona
T. Vermaelen - FC Barcelona

interface​ ​Calculator​ {
​double​ ​clc​(Integer a, Integer b);
}

class​ ​Executor​ {
​public​ ​static​ ExecutorApply ​calculator​(Calculator cal){
​return​ cal::clc;
}
​interface​ ​ExecutorApply​ {
​double​ ​with​(Integer a, Integer b);
}
}

class​ ​MyCal​ {
​public​ ​double​ ​power​(​int​ inputA, ​int​ inputB){
​return​ Executor.calculator((a, b) -> a*b).with(inputA,inputB);
}
}

var result1 = Executor.calculator((a, b) -> a*b).with(​2​,​2​);


var result2 = Executor.calculator((a, b) -> a+b).with(​2​,​6​);
var result3 = Executor.calculator((a, b) -> a/b).with(​6​,​6​);

var cal = n​ ew​ MyCal();


result1+​" "​+result2+​" "​+result3+​" "​+cal.power(​6​, ​10​);

4.0 8.0 1.0 60.0

También podría gustarte