it-swarm.com.ru

Java 8 Pattern Matching?

Будет ли Java 8 поддерживать сопоставление с шаблоном, как Scala и другие функциональные программы? Я собираю презентацию о функциях Lambda в Java 8. Я не могу найти ничего об этой конкретной концепции функционального программирования.

Я помню, что меня заинтересовало функциональное программирование - реализация быстрой сортировки, особенно по сравнению с реализацией императивного программирования.

28
edarroyo

Я предполагаю, что вы говорите не о сопоставлении с образцом в смысле применения регулярного выражения к строке, а о том, как применяется в Haskell . Например, используя подстановочные знаки:

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

Java 8 не будет поддерживать это изначально, с помощью лямбда-выражения, однако, есть способы сделать это, например, для вычисления факториала:

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

Как реализовать это, вы найдете больше информации в этом блоге: Навстречу сопоставлению с образцом в Java .

30
Konrad Reiche

Можно реализовать сопоставление с образцом в виде библиотеки в Java 8 (используя преимущества лямбда-выражений), но, к сожалению, нам все еще не хватает проверки полноты компилятора, которую имеют такие языки, как Haskell или Scala.

Cyclops-реагировать имеет мощный Pattern Matching модуль, который предлагает как структурное сопоставление с образцом для Java 8, так и сопоставление с образцом через охранники.

Он обеспечивает DSL, когда/затем/иначе, и сопоставление, включая деконструкцию, основано на стандартных предикатах Java (поэтому сопоставление можно использовать, например, для фильтрации потока).

Соответствие охранниками

Для сопоставления через охранники мы используем whenGuard/then/else, чтобы ясно показать, что дело движет тест, а не структура тестируемого объекта.

например Для сопоставления на основе защиты: если мы реализуем класс Case, который реализует интерфейс Matchable

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

(кстати, Lombok может пригодиться для создания закрытых иерархий классов дел)

Мы можем сопоставить его внутренние значения (рекурсивно, если необходимо, или по типу среди различных других опций).

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

Если у нас есть объект, который не реализует [Matchable] [3], мы можем в любом случае привести его к Matchable, наш код станет 

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

Если мы не заботимся об одном из значений, мы можем использовать подстановочные знаки 

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

Или рекурсивно де-структурировать вложенный набор классов

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

Где NestedCase выглядит примерно так:

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

Пользователи также могут составлять выражения сопоставления с образцом, используя 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"));

Структурное сопоставление с образцом

Мы также можем сопоставить точную структуру проверяемого объекта. Это вместо того, чтобы использовать if/then тесты, чтобы увидеть, соответствует ли структура нашим случаям, мы можем заставить компилятор гарантировать, что наши случаи соответствуют структуре предоставленных Объектов. DSL для этого почти идентичен в соответствии с защитным соответствием, но мы используем когда/то/иное, чтобы четко показать структуру объектов, управляющую тестовыми примерами, а не наоборот.

  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"

Структурное сопоставление объекта адреса, извлеченного из клиента. Где классы Customer и Address выглядят так

@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));
    }
}

циклоп-реакция обеспечивает Matchables класс, который позволяет структурное сопоставление с общими типами JDK.

9
John McClean

Я знаю, что на этот вопрос уже был дан ответ, кроме того, я новичок в функциональном программировании, но, после долгих колебаний, я наконец решил принять участие в этой дискуссии, чтобы получить обратную связь о том, что следует.

Я бы предложил (тоже?) Простую реализацию ниже. Это немного отличается от (Хорошей) статьи, процитированной в принятом ответе; но по моему (короткому) опыту, он был немного более гибок в использовании и прост в обслуживании (что, конечно, тоже вопрос вкуса).

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 - это библиотека, нацеленная на поддержку почти нативной поддержки типов сумм и сопоставления структурных шаблонов для Java (и даже больше) . Взяв в качестве примера небольшой DSL калькулятора, с Derive4J вы можете написать следующий код:

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