variable parameter expressions example anymatch java lambda java-8 matching

parameter - lambda expressions java



¿Coincidencia de patrones de Java 8? (4)

¿Compatirá Java 8 con la coincidencia de patrones como Scala y otros programas funcionales? Estoy organizando una presentación de las características Lambda de Java 8. No puedo encontrar nada en este concepto particular de programación funcional.

Recuerdo que lo que me interesó de la programación funcional fue la implementación de quicksort, especialmente en comparación con la implementación de la programación imperativa.


Derive4J es una biblioteca que apunta a apoyar soporte casi nativo para tipos de suma y coincidencia de patrones estructurales para Java (y aún más). Tomando una pequeña calculadora DSL como ejemplo, con Derive4J puede escribir el siguiente código:

import java.util.function.Function; import org.derive4j.Data; import static org.derive4j.exemple.Expressions.*; @Data public abstract class Expression { interface Cases<R> { R Const(Integer value); R Add(Expression left, Expression right); R Mult(Expression left, Expression right); R Neg(Expression expr); } public abstract <R> R match(Cases<R> cases); private static Function<Expression, Integer> eval = Expressions .match() .Const(value -> value) .Add((left, right) -> eval(left) + eval(right)) .Mult((left, right) -> eval(left) * eval(right)) .Neg(expr -> -eval(expr)); public static Integer eval(Expression expression) { return eval.apply(expression); } public static void main(String[] args) { Expression expr = Add(Const(1), Mult(Const(2), Mult(Const(3), Const(3)))); System.out.println(eval(expr)); // (1+(2*(3*3))) = 19 } }


Es posible implementar la coincidencia de patrones como una biblioteca en Java 8 (aprovechando las expresiones lambda), pero desafortunadamente todavía nos falta la verificación exhaustiva del compilador que tienen los lenguajes como Haskell o Scala.

Cyclops-react tiene un potente módulo Pattern Matching , que ofrece una coincidencia de patrón estructural para Java 8 y una coincidencia de patrones mediante guardias.

Proporciona un DSL cuando y luego / de otra manera, y la coincidencia, incluida la deconstrucción, se basa en Predicados de Java estándar (por lo que la coincidencia se puede utilizar para filtrar un Stream, por ejemplo).

Coincidencia por guardias

Para hacer coincidir a través de guardias usamos whenGuard / then / otherwise para mostrar claramente que el caso está conduciendo la prueba y no la estructura del Objeto bajo prueba.

por ejemplo, para la coincidencia basada en guardias, si implementamos una clase Case que implementa la interfaz Matchable

static class MyCase implements Matchable{ int a; int b; int c;}

(por cierto, Lombok puede ser muy útil para crear jerarquías de clase de caso selladas)

Podemos hacer coincidir sus valores internos (recursivamente si es necesario, o por tipo entre varias otras opciones).

import static com.aol.cyclops.control.Matchable.otherwise; import static com.aol.cyclops.control.Matchable.whenGuard; new MyCase(1,2,3).matches(c->c.is(whenGuard(1,2,3)).then("hello"), .is(whenGuard(4,5,6)).then("goodbye") ,otherwise("goodbye") );

Si tenemos un Objeto que no implementa [Matchable] [3], podemos forzarlo a Matchable de todos modos, nuestro código se volvería

Matchable.ofDecomposable(()->new MyCase(1,2,3))) .matches(c->c.is(whenGuard(1,2,3)).then("hello"), .is(whenGuard(4,5,6)).then("goodbye") ,otherwise("hello"));

Si no nos importa uno de los valores, podemos usar comodines

new MyCase(1,2,3).matches(c->c.is(whenGuard(1,__,3)).then("hello"), .is(whenGuard(4,__,6)).then("goodbye") ,otherwise("hello) );

O desestructurar recursivamente un conjunto anidado de clases

Matchable.of(new NestedCase(1,2,new NestedCase(3,4,null))) .matches(c->c.is(whenGuard(1,__,has(3,4,__)).then("2") ,otherwise("default");

Donde NestedCase se ve así:

class NestedCase implemends Decomposable { int a; int b; NestedCase c; }

Los usuarios también pueden componer expresiones de coincidencia de patrones usando hamcrest

import static com.aol.cyclops.control.Matchable.otherwise; import static com.aol.cyclops.control.Matchable.then; import static com.aol.cyclops.control.Matchable.when; Matchable.of(Arrays.asList(1,2,3)) .matches(c->c.is(when(equalTo(1),any(Integer.class),equalTo(4))) .then("2"),otherwise("default"));

Coincidencia de patrón estructural

También podemos coincidir en la estructura exacta del Objeto que se prueba. Eso es más que el uso si / entonces pruebas para ver si la estructura coincide con nuestros casos, podemos hacer que el compilador se asegure de que nuestros casos coincidan con la estructura de los Objetos proporcionados. El DSL para hacer esto es casi idéntico por coincidencia basada en guardia, pero usamos cuando / entonces / de otra manera para mostrar claramente que la estructura de Objetos impulsa los casos de prueba y no al revés.

import static com.aol.cyclops.control.Matchable.otherwise; import static com.aol.cyclops.control.Matchable.then; import static com.aol.cyclops.control.Matchable.when; String result = new Customer("test",new Address(10,"hello","my city")) .match() .on$_2() .matches(c->c.is(when(decons(when(10,"hello","my city"))),then("hello")), otherwise("miss")).get(); //"hello"

Conversión estructural en un objeto de dirección extraído de un cliente. Donde las clases de Cliente y Dirección se ven así

@AllArgsConstructor static class Address{ int house; String street; String city; public MTuple3<Integer,String,String> match(){ return Matchable.from(()->house,()->street,()->city); } } @AllArgsConstructor static class Customer{ String name; Address address; public MTuple2<String,MTuple3<Integer,String,String>> match(){ return Matchable.from(()->name,()->Maybe.ofNullable(address).map(a->a.match()).orElseGet(()->null)); } }

cyclops- reaction proporciona una clase de Matchables que permite la coincidencia de patrones estructurales con tipos comunes de JDK.


Soy consciente de que esta pregunta ya ha sido respondida, además soy nuevo en la programación funcional pero, después de muchas dudas, finalmente decidí involucionarme en esta discusión para obtener retroalimentación sobre lo que sigue.

Yo sugeriría la implementación (¿demasiado?) A continuación. Es ligeramente diferente del (agradable) artículo citado en la respuesta aceptada; pero en mi (corta) experiencia, fue un poco más flexible de usar y fácil de mantener (que, por supuesto, también es una cuestión de gusto).

import java.util.Optional; import java.util.function.Function; import java.util.function.Predicate; final class Test { public static final Function<Integer, Integer> fact = new Match<Integer>() .caseOf( i -> i == 0, i -> 1 ) .otherwise( i -> i * Test.fact.apply(i - 1) ); public static final Function<Object, String> dummy = new Match<Object>() .caseOf( i -> i.equals(42), i -> "forty-two" ) .caseOf( i -> i instanceof Integer, i -> "Integer : " + i.toString() ) .caseOf( i -> i.equals("world"), i -> "Hello " + i.toString() ) .otherwise( i -> "got this : " + i.toString() ); public static void main(String[] args) { System.out.println("factorial : " + fact.apply(6)); System.out.println("dummy : " + dummy.apply(42)); System.out.println("dummy : " + dummy.apply(6)); System.out.println("dummy : " + dummy.apply("world")); System.out.println("dummy : " + dummy.apply("does not match")); } } final class Match<T> { public <U> CaseOf<U> caseOf(Predicate<T> cond, Function<T, U> map) { return this.new CaseOf<U>(cond, map, Optional.empty()); } class CaseOf<U> implements Function<T, Optional<U>> { private Predicate<T> cond; private Function<T, U> map; private Optional<CaseOf<U>> previous; CaseOf(Predicate<T> cond, Function<T, U> map, Optional<CaseOf<U>> previous) { this.cond = cond; this.map = map; this.previous = previous; } @Override public Optional<U> apply(T value) { Optional<U> r = previous.flatMap( p -> p.apply(value) ); return r.isPresent() || !cond.test(value) ? r : Optional.of( this.map.apply(value) ); } public CaseOf<U> caseOf(Predicate<T> cond, Function<T, U> map) { return new CaseOf<U>(cond, map, Optional.of(this)); } public Function<T,U> otherwise(Function<T, U> map) { return value -> this.apply(value) .orElseGet( () -> map.apply(value) ); } } }


Supongo que no está hablando de la coincidencia de patrones en el sentido de aplicar una expresión regular en una cadena, sino como se aplica en Haskell . Por ejemplo, usando comodines:

head (x:_) = x tail (_:xs) = xs

Java 8 no lo admitirá de forma nativa, sin embargo, con la expresión Lambda hay formas de hacerlo, como esta para calcular el factorial:

public static int fact(int n) { return ((Integer) new PatternMatching( inCaseOf(0, _ -> 1), otherwise( _ -> n * fact(n - 1)) ).matchFor(n)); }

Cómo implementar eso encontrará más información en esta publicación de blog: Towards Pattern Matching en Java .