如何使用Java 8编写函数的泛型迭代?

时间:2014-05-24 21:48:07

标签: java generics java-8 functor

请考虑以下两个功能:

public static <X, Y, U, V extends X> Function<U, Y> composite(
    Function<X, Y> first, Function<U, V> second)
{
    Objects.requireNonNull(first);
    Objects.requireNonNull(second);
    return (U arg) -> first.apply(second.apply(arg));
}

public static <X, Y extends X> Function<X, ?> iterate(Function<X, Y> function, int n)
{
    if (n < 0)
        return null;
    if (n == 0)
        return (X arg) -> arg;  
    Objects.requireNonNull(function);

    Function<X, Y> iteration = function;
    for (; n > 1; --n)
        iteration = composite(function, iteration);
    return iteration;
}

虽然composite(first, second)计算firstsecond的{​​{3}},但iterate(function, n)计算nth composition function }}

虽然限制Y extends X适用于任何n > 0,但我们对n == 0遇到了一些问题。在数学上,iterate应该产生身份函数。但是,我们也需要X extends Y,即X == Y

请考虑以下示例

Function<Double, Double> nthSquareRoot = iterate(Math::sqrt, n);

这会产生错误消息:

Type mismatch: cannot convert from Function<Double,capture#2-of ?> to Function<Double,Double>

这里最好的选择是什么?如果n == 1,我们可以检查X是否可以接受为Y。我想听听其他选项和一些想法如何执行此检查(据我所知,没有简单的解决方案来检查两个通用参数是否相等)。

2 个答案:

答案 0 :(得分:8)

尝试:

public static<X> UnaryOperator<X> iterate(UnaryOperator<X> f, int n) { ... }

如果Y&lt;:X,那么从X到Y的函数也是从X到X的函数,你应该能够做你想做的事。

答案 1 :(得分:5)

您需要更改结果的类型以考虑通配符:

Function<Double, ? extends Double> nthSquareRoot = iterate(Math::sqrt, 2);

以及iterate方法的签名:

public static <X, Y extends X> Function<X, ? extends X> iterate(Function<X, Y> function, int n)

示例:

public static <X, Y extends X> Function<X, ? extends X> iterate(Function<X, Y> function, int n) {
  if (n < 0) return null;
  if (n == 0) return Function.identity();

  Function<X, Y> iteration = Objects.requireNonNull(function);
  for (; n > 1; --n) {
    iteration = composite(function, iteration);
  }
  return iteration;
}

public static void main(String[] args) {
  Function<Double, ? extends Double> nthSquareRoot = iterate(Math::sqrt, 2);
  System.out.println(nthSquareRoot.apply(81d));
}

按预期打印9个。


或者,您可以使用UnaryOperator

public static <X> UnaryOperator<X> iterate(UnaryOperator<X> function, int n) {
  if (n < 0) return null;
  if (n == 0) return UnaryOperator.identity();

  UnaryOperator<X> iteration = Objects.requireNonNull(function);
  for (int i = 0; i < n - 1; i++) {
    iteration = composite(function::apply, iteration::apply)::apply;
  }
  return iteration;
}

public static void main(String[] args) {
  UnaryOperator<Double> nthSquareRoot = iterate(Math::sqrt, 2);
  System.out.println(nthSquareRoot.apply(81d));
}