web-dev-qa-db-de.com

Java 8 Pattern Matching?

Unterstützt Java 8 Pattern Matching wie Scala und andere funktionale Programme? Ich stelle eine Präsentation der Lambda-Funktionen von Java 8 zusammen. Ich kann zu diesem speziellen Konzept der funktionalen Programmierung nichts finden.

Ich erinnere mich, was mich an funktionaler Programmierung interessiert hat, war die QuickSort-Implementierung, insbesondere im Vergleich zur Implementierung der imperativen Programmierung.

28
edarroyo

Ich nehme an, Sie sprechen nicht von Pattern Matching im Sinne der Anwendung eines regulären Ausdrucks auf einen String, sondern wie in Haskell angewendet. Zum Beispiel mit Platzhaltern:

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

Java 8 unterstützt dies nicht nativ, mit Lambda-Ausdrücken gibt es jedoch Möglichkeiten, dies wie folgt zu tun, um die Fakultät zu berechnen:

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

Wie Sie das implementieren können, erfahren Sie in diesem Blog-Beitrag: Towards Pattern Matching in Java .

30
Konrad Reiche

Es ist möglich, den Musterabgleich als Bibliothek in Java 8 zu implementieren (wobei Lambda-Ausdrücke genutzt werden), aber leider wird immer noch die vollständige Compiler-Prüfung fehlen, die Sprachen wie Haskell oder Scala haben.

Cyclops-reag hat ein leistungsfähiges Pattern Matching - Modul, das sowohl strukturelle Musteranpassung für Java 8 als auch Musteranpassung über Guards bietet.

Es bietet ein Wann/Dann/Andernfalls DSL und Matching, einschließlich Dekonstruktion basiert auf Standard-Java-Prädikaten (sodass Matching beispielsweise zum Filtern eines Streams verwendet werden kann).

Zusammenbringen von Wachen

Für den Abgleich über Schutzeinrichtungen verwenden wir whenGuard/then/anderweitig, um deutlich zu zeigen, dass der Fall den Test steuert und nicht die Struktur des zu testenden Objekts.

z.B. Für ein Guard-basiertes Matching, wenn wir eine Case-Klasse implementieren, die die Matchable-Schnittstelle implementiert

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

(Übrigens, Lombok kann sehr nützlich sein, um Hierarchien mit versiegelten Fallklassen zu erstellen.)

Wir können die internen Werte anpassen (rekursiv, falls erforderlich oder nach Typ unter verschiedenen anderen Optionen).

  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")
                           );

Wenn wir ein Objekt haben, das [Matchable] [3] nicht implementiert, können wir es trotzdem zu Matchable zwingen, unser Code würde zu 

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"));

Wenn wir uns nicht für einen der Werte interessieren, können wir Platzhalter verwenden 

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

Oder strukturieren Sie rekursiv eine verschachtelte Menge von Klassen

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

Wo NestedCase so aussieht -

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

Benutzer können auch Mustervergleichsausdrücke mithilfe von hamcrest erstellen

 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"));

Strukturelles Mustervergleich

Wir können auch die genaue Struktur des getesteten Objekts anpassen. Dies ist eher als ein Gebrauch von if/then, um zu prüfen, ob die Struktur unseren Fällen entspricht. Der Compiler kann sicherstellen, dass unsere Fälle der Struktur der bereitgestellten Objekte entsprechen. Das DSL dafür ist fast identisch mit dem pro Wächter basierenden Matching, aber wir verwenden wann/then/anderweitig, um die Objektstruktur eindeutig zu zeigen und die Testfälle zu steuern, und nicht umgekehrt.

  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"

Struktureller Abgleich für ein Adressobjekt, das von einem Kunden extrahiert wurde. Wo sehen die Kunden- und Adressklassen dies aus?

@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 -rea stellt eine Matchables -Klasse bereit, die einen Strukturmusterabgleich mit gängigen JDK-Typen ermöglicht.

9
John McClean

Ich bin mir bewusst, dass diese Frage bereits beantwortet wurde, außerdem bin ich neu in der funktionalen Programmierung, aber nach langem Zögern habe ich mich schließlich entschlossen, mich in diese Diskussion einlassen zu lassen, um eine Rückmeldung zu erhalten, was folgt.

Ich würde die (auch?) Einfache Implementierung unten vorschlagen. Es unterscheidet sich geringfügig von dem (netten) Artikel, der in der akzeptierten Antwort zitiert wird. In meiner (kurzen) Erfahrung war es jedoch etwas flexibler zu verwenden und leicht zu warten (was natürlich auch Geschmackssache ist).

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) );
        }
    }
}
2
user7272806

Derive4J ist eine Bibliothek mit dem Ziel, Unterstützung für Summenarten und Strukturmusterabgleich für Java (und noch mehr) zu unterstützen. Am Beispiel eines kleinen DSL-Rechners können Sie mit Derive4J dies tun schreibe den folgenden Code:

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
    }
}
0
JbGi