Java 8 Pattern Matching?

Java 8 prendra-t-il en charge le filtrage par motif comme Scala et d’autres programmes fonctionnels? Je prépare une présentation des fonctionnalités Lambda de Java 8. Je ne trouve rien sur ce concept de functional programming particulier.

Je me souviens que ce qui m’a intéressé dans la functional programming, c’est l’implémentation rapide, en particulier par rapport à l’implémentation de la programmation impérative.

Je suppose que vous ne parlez pas de correspondance de motif dans le sens où vous appliquez une expression régulière à une chaîne, mais comme appliqué dans Haskell . Par exemple en utilisant des jokers:

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

Java 8 ne supportera pas cela de manière native, avec l’expression Lambda, il y a cependant plusieurs façons de le faire, comme ceci pour calculer la factorielle:

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

Comment mettre en œuvre que vous trouverez plus d’informations dans cet article de blog: Vers la correspondance de modèle en Java .

Il est possible d’implémenter une correspondance de modèle en tant que bibliothèque dans Java 8 (en tirant parti des expressions lambda), mais malheureusement, nous ne pourrons toujours pas vérifier l’exhaustivité du compilateur que possèdent les langages tels que Haskell ou Scala.

Cyclops-react dispose d’un puissant module de correspondance de modèles , qui offre à la fois des modèles de correspondance structurelle pour Java 8 et des modèles via des gardes.

Il fournit une connexion DSL quand / alors / sinon, et la correspondance, y compris la déconstruction, est basée sur les prédicats Java standard (la correspondance peut donc être utilisée pour filtrer un stream, par exemple).

Matching par des gardes

Pour l’appariement via des gardes, nous utilisons whenGuard / then / sinon pour montrer clairement que le cas pilote le test et non la structure de l’object à tester.

Par exemple, pour la correspondance basée sur la protection, si nous implémentons une classe Case implémentant l’interface

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

(btw, Lombok peut être très utile pour créer des hiérarchies de classes de cas scellées)

Nous pouvons faire correspondre ses valeurs internes (récursivement si nécessaire, ou par type parmi diverses autres options).

  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 nous avons un object qui n’implémente pas [Matchable] [3], nous pouvons quand même le contraindre à Matchable, notre code deviendrait

 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 nous ne nous soucions pas de l’une des valeurs, nous pouvons utiliser des caractères génériques

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

Ou déstructurer de manière récursive un ensemble nested de classes

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

Où NestedCase ressemble à ceci –

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

Les utilisateurs peuvent également composer des expressions de correspondance de modèle à l’aide de 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")); 

Correspondance structurelle

Nous pouvons également faire correspondre la structure exacte de l’object à tester. C’est plutôt que d’utiliser si / alors des tests pour voir si la structure correspond à nos cas, nous pouvons demander au compilateur de nous assurer que nos cas correspondent à la structure des objects fournis. Pour ce faire, le DSL est presque identique selon la comparaison basée sur la garde, mais nous utilisons quand / alors / sinon pour montrer clairement la structure Objects conduit les scénarios de test et non l’inverse.

  import static com.aol.cyclops.control.Matchable.otherwise; import static com.aol.cyclops.control.Matchable.then; import static com.aol.cyclops.control.Matchable.when; Ssortingng 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" 

Correspondance structurelle sur un object d’adresse extrait d’un client. Là où les classes Customer et Address ont ceci ressemblent

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

cyclops- react fournit une classe Matchables qui permet la correspondance de motifs structurels avec les types JDK courants.

Derive4J est une bibliothèque qui vise à prendre en charge une prise en charge quasi native des types de sum et des correspondances de modèles structurels pour Java (et même davantage). En prenant une petite calculasortingce DSL comme exemple, avec Derive4J vous pouvez écrire le code suivant:

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

Je suis conscient que cette question a déjà été répondue, en outre je suis nouveau dans la functional programming mais, après beaucoup d’hésitation, j’ai finalement décidé de m’investir dans cette discussion pour avoir un retour sur ce qui suit.

Je suggérerais la (trop?) Simple implémentation ci-dessous. Il diffère légèrement du (bel) article cité dans la réponse acceptée; mais d’après mon expérience (courte), il était un peu plus souple d’utilisation et facile à entretenir (ce qui est bien sûr aussi une question de goût).

 import java.util.Optional; import java.util.function.Function; import java.util.function.Predicate; final class Test { public static final Function fact = new Match() .caseOf( i -> i == 0, i -> 1 ) .otherwise( i -> i * Test.fact.apply(i - 1) ); public static final Function dummy = new Match() .caseOf( i -> i.equals(42), i -> "forty-two" ) .caseOf( i -> i instanceof Integer, i -> "Integer : " + i.toSsortingng() ) .caseOf( i -> i.equals("world"), i -> "Hello " + i.toSsortingng() ) .otherwise( i -> "got this : " + i.toSsortingng() ); public static void main(Ssortingng[] 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 { public  CaseOf caseOf(Predicate cond, Function map) { return this.new CaseOf(cond, map, Optional.empty()); } class CaseOf implements Function> { private Predicate cond; private Function map; private Optional> previous; CaseOf(Predicate cond, Function map, Optional> previous) { this.cond = cond; this.map = map; this.previous = previous; } @Override public Optional apply(T value) { Optional r = previous.flatMap( p -> p.apply(value) ); return r.isPresent() || !cond.test(value) ? r : Optional.of( this.map.apply(value) ); } public CaseOf caseOf(Predicate cond, Function map) { return new CaseOf(cond, map, Optional.of(this)); } public Function otherwise(Function map) { return value -> this.apply(value) .orElseGet( () -> map.apply(value) ); } } }