Scala的Partial函数有Java等价物吗?

ymzxtsji  于 6个月前  发布在  Scala
关注(0)|答案(3)|浏览(82)

Scala有partial functions,它们是只适用于输入类型的某些值的函数,但不是全部:

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允许将“partialness”应用于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中的等价物是什么?

大多数谷歌搜索结果混淆了“部分函数”和柯里化,例如“部分应用函数”。

xsuvu9jc

xsuvu9jc1#

一种Java 8惯用但不完全忠实的方法

Java 8中最典型的是Optional类:

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

字符串
如果你熟悉Optional,那么你会看到字符串连接i + " is even"只有在过滤条件i % 2 == 0为true时才被计算。如果你不熟悉Java的Optional,你也可以用if/else来写:

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


这应该完全清楚,当且仅当保护条件计算为true时,才计算字符串串联。

一种更可靠但不那么惯用的方法(Java)

使用简单的方法,您调用函数,并获得一个Optional,它要么包含函数的实际值,要么不包含。但Scala的PartialFunction稍微复杂一点。从您链接到的文档中:
即使isDefinedAt为a:A返回true,调用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));
  }

gstyhher

gstyhher2#

Java似乎没有直接提供PartialFunction,但它提供了一些接口,你可以用它们来定义自己的PartialFunction<X,Y>PartialFunction<X, Y>本质上就是:

  • Predicate<X>,用于测试X类型的值是否在域中
  • Function<X, Y>用于实际调用的函数定义 * 如果 * 函数是为参数值定义的。

下面,我简要介绍了如何使用PredicateFunction来实现PartialFunction。在这个草图中,我只定义了三个最重要的方法:

  • isDefinedAt本质上就是Predicate s test
  • apply调用test,如果成功,则调用applyIfDefined,它表示“函数的实际主体”(Scala中case s的右侧)
  • orElse演示了fallback-behavior的组合结构(与普通函数组合非常不同)

以下是完整的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类的实现。

fcy6dtqo

fcy6dtqo3#

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

字符串
更复杂的例子你可以找到here

相关问题