使用 Java 8 实现递归 lambda 函数
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/19429667/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Implement recursive lambda function using Java 8
提问by user2678835
Java 8 introduced lambda functions and I want to implement something like factorial:
Java 8 引入了 lambda 函数,我想实现类似阶乘的东西:
IntToDoubleFunction fact = x -> x == 0 ? 1 : x * fact.applyAsDouble(x-1);
Compilation returns
编译返回
error: variable fact might not have been initialized
How can I reference function itself. Class is anonymous but instance exists: It is called fact
.
我如何引用函数本身。类是匿名的,但实例存在:它被称为fact
。
回答by user2678835
One solution is to define this function as an INSTANCE attribute.
一种解决方案是将此函数定义为 INSTANCE 属性。
import java.util.function.*;
public class Test{
IntToDoubleFunction fact = x -> { return ( x == 0)?1:x* fact.applyAsDouble(x-1);};
public static void main(String[] args) {
Test test = new Test();
test.doIt();
}
public void doIt(){
System.out.println("fact(3)=" + fact.applyAsDouble(3));
}
}
回答by user2678835
Another version using accumulator so that recursion can be optimised. Moved to Generic interface definition.
使用累加器的另一个版本,以便可以优化递归。移至通用接口定义。
Function<Integer,Double> facts = x -> { return ( x == 0)?1:x* facts.apply(x-1);};
BiFunction<Integer,Double,Double> factAcc= (x,acc) -> { return (x == 0)?acc:factAcc.apply(x- 1,acc*x);};
Function<Integer,Double> fact = x -> factAcc.apply(x,1.0) ;
public static void main(String[] args) {
Test test = new Test();
test.doIt();
}
public void doIt(){
int val=70;
System.out.println("fact(" + val + ")=" + fact.apply(val));
}
}
回答by newacct
Local and anonymous classes, as well as lambdas, capture local variables by valuewhen they are created. Therefore, it is impossible for them to refer to themselves by capturing a local variable, because the value for pointing to themself does not exist yet at the time they are being created.
本地和匿名类以及 lambda 表达式在创建时按值捕获本地变量。因此,它们不可能通过捕获局部变量来引用自己,因为在它们被创建时指向自己的值还不存在。
Code in local and anonymous classes can still refer to themselves using this
. However, this
in a lambda does not refer to the lambda; it refers to the this
from the outside scope.
本地和匿名类中的代码仍然可以使用this
. 但是,this
在 lambda 中并不指代 lambda;它指的是this
来自外部的范围。
You could capture a mutable data structure, like an array, instead:
您可以改为捕获可变数据结构,例如数组:
IntToDoubleFunction[] foo = { null };
foo[0] = x -> { return ( x == 0)?1:x* foo[0].applyAsDouble(x-1);};
though hardly an elegant solution.
虽然不是一个优雅的解决方案。
回答by assylias
You can define a recursive lambda as an instance or class variable:
您可以将递归 lambda 定义为实例或类变量:
static DoubleUnaryOperator factorial = x -> x == 0 ? 1
: x * factorial.applyAsDouble(x - 1);
for example:
例如:
class Test {
static DoubleUnaryOperator factorial = x -> x == 0 ? 1
: x * factorial.applyAsDouble(x - 1));
public static void main(String[] args) {
System.out.println(factorial.applyAsDouble(5));
}
}
prints 120.0
.
打印120.0
。
回答by Danil Gaponov
public class Main {
static class Wrapper {
Function<Integer, Integer> f;
}
public static void main(String[] args) {
final Wrapper w = new Wrapper();
w.f = x -> x == 0 ? 1 : x * w.f.apply(x - 1);
System.out.println(w.f.apply(10));
}
}
回答by rationalis
One way is to write a secondary function, helper
, which takes a function and a number as arguments, and then write the function you actually want, fact = helper(helper,x)
.
一种方法是编写一个辅助函数 ,helper
它接受一个函数和一个数字作为参数,然后编写你真正想要的函数fact = helper(helper,x)
。
Like so:
像这样:
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);
This seems to me to be slightly more elegant than relying on corner case semantics like a closure that captures a reference to a mutable structure, or allowing self-reference with a warning of the possibility of "might not be initialized."
在我看来,这比依赖极端情况语义(例如捕获对可变结构的引用的闭包)或允许自我引用并警告“可能无法初始化”的可能性要稍微优雅一些。
Still, it's not a perfect solution because of Java's type system -- the generics cannot guarantee that f
, the argument to factHelper
, is of the same type as factHelper
(i.e. same input types and output types), since that would be an infinitely nested generic.
尽管如此,由于 Java 的类型系统,它不是一个完美的解决方案——泛型不能保证f
, 的参数factHelper
,与 具有相同的类型factHelper
(即相同的输入类型和输出类型),因为那将是一个无限嵌套的泛型。
Thus, instead, a safer solution might be:
因此,相反,更安全的解决方案可能是:
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);
};
The code smell incurred from factHelper
's less-than-perfect generic type is now contained (or, dare I say, encapsulated) within the lambda, ensuring that factHelper
will never be called unknowingly.
由factHelper
不完美的泛型类型引起的代码异味现在包含(或者,我敢说,封装)在 lambda 中,确保factHelper
永远不会在不知不觉中被调用。
回答by shrini1000
I don't have a Java8 compiler handy, so can't test my answer. But will it work if you define the 'fact' variable to be final?
我手边没有 Java8 编译器,所以无法测试我的答案。但是,如果您将“事实”变量定义为最终变量,它会起作用吗?
final IntToDoubleFunction fact = x -> {
return ( x == 0)?1:x* fact.applyAsDouble(x-1);
};
回答by Jerrolds
The following works but it does seem arcane.
以下工作,但它似乎很神秘。
import java.util.function.Function;
class recursion{
Function<Integer,Integer> factorial_lambda; // The positions of the lambda declaration and initialization must be as is.
public static void main(String[] args) { new recursion();}
public recursion() {
factorial_lambda=(i)->{
if(i==1)
return 1;
else
return i*(factorial_lambda.apply(i-1));
};
System.out.println(factorial_lambda.apply(5));
}
}
// Output 120
回答by beck
I heard at the JAX this year, that "lambads do not support recursion". What is meant with this statement is that the "this" inside the lambda always refer to the surrounding class.
我在今年的 JAX 上听说“lambads 不支持递归”。这个语句的意思是 lambda 中的“this”总是指周围的类。
But I managed to define - at least how I understand the term "recursion" - a recursive lambda and it goes like that:
但是我设法定义了——至少我是如何理解“递归”这个词的——一个递归的 lambda,它是这样的:
interface FacInterface {
int fac(int i);
}
public class Recursion {
static FacInterface f;
public static void main(String[] args)
{
int j = (args.length == 1) ? new Integer(args[0]) : 10;
f = (i) -> { if ( i == 1) return 1;
else return i*f.fac( i-1 ); };
System.out.println( j+ "! = " + f.fac(j));
}
}
Save this inside a file "Recursion.java" and with the two commands "javac Recursion.java" and "java Recursion" it worked for me.
将其保存在“Recursion.java”文件中,并使用“javac Recursion.java”和“java Recursion”这两个命令对我有用。
The clou is to keep the interface that the lambda has to implement as a field variable in the surrounding class. The lambda can refer to that field and the field will not be implicitly final.
clou 是将 lambda 必须实现的接口保留为周围类中的字段变量。lambda 可以引用该字段,并且该字段不会是隐式最终的。
回答by Rene.v.P.
A bit like the very first reply ...
有点像第一个回复...
public static Function<Integer,Double> factorial;
static {
factorial = n -> {
assert n >= 0;
return (n == 0) ? 1.0 : n * factorial.apply(n - 1);
};
}