Есть ли у Scala Partial Function Java-эквивалент? - PullRequest
0 голосов
/ 12 октября 2018

Scala имеет частичные функции , которые являются функциями, которые применяются только к некоторым значениям типа ввода, но не ко всем:

val isEven: PartialFunction[Int, String] = {
  case x if x % 2 == 0 => x+" is even"
} 

assert(isEven(10) equalsIgnoreCase "10 is even")
assert(isEven.isDefinedAt(11) == false)

И, что еще более полезно, Scala позволяет "частичность "для применения к подтипу trait:

sealed trait BaseTrait

case class Foo(i : Int) extends BaseTrait
case class Bar(s : String) extends BaseTrait

val fooPartialFunc : PartialFunction[BaseTrait, Int] = {
  case f : Foo => 42 + f.i
}

assert(fooPartialFunc(Foo(8)) == 50)
assert(fooPartialFunc.isDefinedAt(Bar("hello")) == false)

Что эквивалентно в java 8?

Большинство результатов Google приводят в замешательство"частичная функция "с каррированием, например," частично примененная функция ".

Ответы [ 3 ]

0 голосов
/ 13 октября 2018

Java, кажется, не предоставляет PartialFunction напрямую, но предоставляет несколько интерфейсов, которые вы можете использовать для определения своих PartialFunction<X,Y>.A PartialFunction<X, Y> по существу просто:

  • a Predicate<X> для проверки, находится ли значение типа X в домене
  • Function<X, Y> для фактического определения функции, котороевызывается , если функция определена для значения аргумента.

Ниже я обрисовал, как можно использовать Predicate и Function для реализации PartialFunction.В этом наброске я определил только три наиболее важных метода:

  • isDefinedAt по сути просто Predicate s test.
  • apply вызывает test ив случае успеха вызывает applyIfDefined, который представляет «фактическое тело функции» (правая часть case s в Scala)
  • orElse - это демонстрация композиционной структуры, отличной отобычная композиция функций, просто как подтверждение концепции

Вот полный код Java:

import java.util.function.*;

abstract class PartialFunction<X, Y> implements Predicate<X>, Function<X, Y> {
  public boolean isDefinedAt(X x) {
    return this.test(x);
  }
  public Y apply(X x) {
    if (isDefinedAt(x)) {
      return applyIfDefined(x);
    } else {
      throw new IllegalArgumentException("Match error on " + x);
    }
  }
  public abstract Y applyIfDefined(X x);

  public PartialFunction<X, Y> orElse(PartialFunction<X, Y> fallback) {
    PartialFunction<X, Y> outer = this;
    return new PartialFunction<X, Y>(){
      public boolean test(X x) {
        return outer.test(x) || fallback.test(x);
      }
      public Y applyIfDefined(X x) {
        if (outer.isDefinedAt(x)) {
          return outer.applyIfDefined(x);
        } else {
          return fallback.apply(x);
        }
      }
      @Override
      public Y apply(X x) {
        return applyIfDefined(x);
      }
    };
  }

  public static void main(String[] args) {
    PartialFunction<Integer, String> f = new PartialFunction<Integer, String>() {
      public boolean test(Integer i) {
        return i % 2 == 0;
      }
      public String applyIfDefined(Integer i) {
        return i + " is even";
      }
    };
    PartialFunction<Integer, String> g = new PartialFunction<Integer, String>() {
      public boolean test(Integer i) {
        return i % 2 == 1;
      }
      public String applyIfDefined(Integer i) {
        return i + " is odd";
      }
    };
    System.out.println(f.apply(42));
    try {
      System.out.println(f.apply(43));
    } catch (Exception e) {
      System.out.println(e);
    }
    PartialFunction<Integer, String> h = f.orElse(g);
    System.out.println(h.apply(100));
    System.out.println(h.apply(101));
  }
}

Вывод приведенного выше примера:

42 is even
java.lang.IllegalArgumentException: Match error on 43
100 is even
101 is odd

Если вы хотите использовать его с какими-то «case-классами», вы можете сделать это тоже, но для этого вам придется сначала обеспечить реализацию классов case.

0 голосов
/ 13 октября 2018

Vavr привносит в Java множество интересных вещей из Scala.

Если вы согласны со сторонними библиотеками, это может быть лучшим выбором.

@Test
public void test() {
    PartialFunction<Integer, String> isEven =
            Function1.<Integer, String>of(integer -> integer + " is even")
                    .partial(integer -> integer % 2 == 0);

    Assert.assertEquals("10 is even", isEven.apply(10));
    Assert.assertFalse(isEven.isDefinedAt(11));
}

Более сложный пример вы можете найти здесь

0 голосов
/ 13 октября 2018

Java 8 идиоматический, но не полностью верный, подход

Наиболее типичной вещью в Java 8 будет дополнительный класс:

import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;

import org.junit.Test;

import java.util.Optional;

public class OptionalAsPartialFunction {

  Optional<String> isEven(final int x) {
    return Optional.of(x)
        .filter(i -> i % 2 == 0)
        .map(i -> i + " is even");
  }

  @Test
  public void example() {
    assertThat(isEven(10).get(), equalTo("10 is even"));
    assertThat(isEven(11).isPresent(), is(false));
  }
}

Если вы знакомы сНеобязательно, тогда вы увидите, что конкатенация строк i + " is even" оценивается, только если условие фильтра i % 2 == 0 выполнено.Если вы не знакомы с Java Optional, вы также можете написать это с помощью if/else:

Optional<String> isEven(final int x) {
  if (x % 2 == 0) {
    return Optional.of(x + " is even");
  } else {
    return Optional.empty();
  }
}

Это должно сделать совершенно ясным, что конкатенация строк оценивается тогда и только тогда, когдаусловие защиты оценивается как истинное.

Более верный, но менее идиоматический (на Java) подход

При наивном подходе вы вызываете функцию и получаете Optional, который либо содержитфактическое значение функции или нет.Но функция PartialFunction в Scala немного сложнее.Из документации, на которую вы ссылались:

Даже если isDefinedAt возвращает true для a: A, вызов apply (a) может все равно вызвать исключение, поэтому следующий код допустим:

val f: PartialFunction[Int, Any] = { case _ => 1/0 }

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

Поэтому более верным подходом было бы использование Optional<Supplier<...>>.Внешний Optional позволяет узнать, есть ли вычисление для выполнения, а внутренний Supplier позволяет вам выполнить это вычисление (если вы выберете).Таким образом, пример будет выглядеть так:

  Optional<Supplier<String>> isEven(final int x) {
    return Optional.of(x)
        .filter(i -> i % 2 == 0)
        .map(i -> () -> i + " is even");
  }

или с if/else:

Optional<Supplier<String>> isEven(final int x) {
  if (x % 2 == 0) {
    return Optional.of(() -> x + " is even");
  } else {
    return Optional.empty();
  }
}

и isPresent() по-прежнему проверяется, определена ли функция, но get() теперь будетвернуть Supplier, чей метод get() фактически вычислит значение:

  @Test
  public void example() {
    assertThat("isPresent() checks whether the function is defined for the input", //
        isEven(10).isPresent(), equalTo(true));
    assertThat("get() returns the supplier that actually computes the value", //
        isEven(10).get().get(), equalTo("10 is even"));
    assertThat("isPresent() checks whether the function is defined for the input", //
        isEven(11).isPresent(), is(false));
  }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...