使用 Java 8 实现递归 lambda 函数
Java 8 引入了 lambda 函数,我想实现类似阶乘的东西:
IntToDoubleFunction fact = x -> x == 0 ? 1 : x * fact.applyAsDouble(x-1);
编译返回
error: variable fact might not have been initialized
如何引用函数本身。类是匿名的,但实例存在:它被称为 。fact
Java 8 引入了 lambda 函数,我想实现类似阶乘的东西:
IntToDoubleFunction fact = x -> x == 0 ? 1 : x * fact.applyAsDouble(x-1);
编译返回
error: variable fact might not have been initialized
如何引用函数本身。类是匿名的,但实例存在:它被称为 。fact
我通常使用(一次性为所有功能接口定义)泛型帮助器类,它包装功能接口类型的变量。这种方法解决了局部变量初始化的问题,并允许代码看起来更清晰。
如果出现此问题,代码将如下所示:
// Recursive.java
// @param <I> - Functional Interface Type
public class Recursive<I> {
public I func;
}
// Test.java
public double factorial(int n) {
Recursive<IntToDoubleFunction> recursive = new Recursive<>();
recursive.func = x -> (x == 0) ? 1 : x * recursive.func.applyAsDouble(x - 1);
return recursive.func.applyAsDouble(n);
}
一种方法是编写一个辅助函数,它将函数和数字作为参数,然后编写您实际想要的函数。helper
fact = helper(helper,x)
这样:
BiFunction<BiFunction, Double, Double> factHelper =
(f, x) -> (x == 0) ? 1.0 : x*(double)f.apply(f,x-1);
Function<Double, Double> fact =
x -> factHelper.apply(factHelper, x);
在我看来,这似乎比依赖角例语义(例如捕获对可变结构的引用的闭包)或允许自我引用并警告“可能未初始化”的可能性稍微优雅一些。
尽管如此,由于Java的类型系统,它不是一个完美的解决方案 -- 泛型不能保证 , 的参数与 (即相同的输入类型和输出类型) 具有相同的类型,因为这将是一个无限嵌套的泛型。f
factHelper
factHelper
因此,相反,更安全的解决方案可能是:
Function<Double, Double> fact = x -> {
BiFunction<BiFunction, Double, Double> factHelper =
(f, d) -> (d == 0) ? 1.0 : d*(double)f.apply(f,d-1);
return factHelper.apply(factHelper, x);
};
从不完美的泛型类型中产生的代码气味现在包含在 lambda 中(或者,我敢说,封装在)中,确保永远不会在不知不觉中被调用。factHelper
factHelper