从 Java 8 开始,Java 不提供尾调用优化 (TCO)。在研究它时,我了解到有一些安全原因导致团队放弃了 TCO,这导致递归代码的堆栈溢出,但可以使用函数式接口和 lambdas 来模拟它。
那么为什么要问 Lambda 呢?嗯,Lambda 是一项非常有趣的工作,因为你看,
lambda 不仅仅是Functional Interfaces.
在编译类时匿名实现的语法糖,它会生成单个文件:Lambda.class.
内部类没有额外的类文件。
Lambdas 是对象引用,它使用了一种叫做 invokedynamic 的东西:
public class Lambda {
public static void main(String[] args){
Engine start=()->
System.out.print("Do you know cats have 9 lives?");
start.doSomeThing();
}
}
interface Engine{
void doSomeThing();
}
现在让我们开始实现,让我们看看这个叫做四的幂的问题.
给定一个整数 n,true
如果它是 4 的幂,则返回。否则,返回false
。
整数n
是 4 的幂,如果存在满足 的x
整数n == 4x
。
现在,如果我们使用普通递归来实现这个问题,它在 Java 8 中会看起来像这样:
class Solution {
public boolean isPowerOfFour(int n) {
Power<Long,Long> power=new Power<>();
power.execute = (num,pow)->{
if(pow==num){return true;}
if(pow>num){return false;}
return power.execute.execute(num,pow*4);
};
return power.execute.execute(n,1);
}
}
@FunctionalInterface
interface Execute{
boolean execute(int num,int pow);
}
class Power<num,pow> {
Execute execute;
}
现在让我们运行代码 -> 我们遇到了我们的老朋友,或者我应该说敌人堆栈溢出
现在让我们看看实现尾递归后的代码:
class Solution {
public boolean isPowerOfFour(int n) {
return new TailCallPower().power(n, 1).invoke();
}
}
class TailCallPower{
//Business Logic
public static TailCall<Boolean> power(final long n, final long current) {
if (current >= n){
//Here insert the output to storage
if(current==n){
return (new Storage()).done(true);
}
return (new Storage()).done(false);
}
else {
return () -> power(n , current*4);
}
}
}
class Storage {
public <T> TailCall<T> done(final T value) {
return new TailCall<T>() {
@Override
public boolean isComplete() {
return true;
}
@Override
public T result() {
return value;
}
@Override
public TailCall<T> apply() {
throw new Error("not implemented");
}
};
}
}
interface TailCall<T> {
TailCall<T> apply();
default boolean isComplete() {
return false;
}
default T result() {
throw new Error("not implemented");
}
default T invoke() {
return Stream.iterate(this, TailCall::apply)
.filter(TailCall::isComplete)
.findFirst()
.get()
.result();
}
}
它被接受了,让我们看看实现细节
让我们一步一步来,这段代码在做什么
类 TailCallPower
我们将在这里编写我们的业务逻辑:这个类只是一个调用的包装器() -> power(n , current*4);
,返回类型是 TailCall 接口所以你最后只是定义了我们的功能接口的定义,为了更好地理解请看下面的代码;
public class Engine {
public static void main(String[] args){
Add<Integer> add=(number1,number2)->{
return number1+number2;
};
System.out.println(add.addition(2,3));
}
}
@FunctionalInterface
interface Add<T>{
T addition(T a,T b);
}
这可以重写为
public class Engine {
public static void main(String[] args){
System.out.println(addition().addition(3,2));
}
public static Add<Integer> addition(){
return (num1,num2)->{
return num1+num2;
};
}
}
@FunctionalInterface
interface Add<T>{
T addition(T a,T b);
}
这可以重写为
public class Engine {
public static void main(String[] args){
System.out.println(addition().addition(3,2));
}
public static Add<Integer> addition(){
return (num1,num2)->num1+num2;
}
}
@FunctionalInterface
interface Add<T>{
T addition(T a,T b);
}
所以这个想法是创建一个接口,其中递归发生在默认方法和业务逻辑中的另一个接口和一个包含两者的包装类
class Storage
顾名思义,在每个递归步骤中都会为我们的结果存储数据
因此,当程序完成时,您只需检索堆栈顶部的元素
等待蛋糕上的樱桃:该算法基于泛型,因此您可以根据需要在从阶乘到 fib 的任何地方实现它。
这是我实施的一些示例
在java中使用尾端递归的斐波那契示例
class Solution {
public int fib(int n) {
return Math.toIntExact(new TailCallPower().fib(n,0,1).invoke());
}
}
class TailCallPower{
//Business Logic
public static TailCall<Long> fib(final long n,final long a,final long b) {
if (n == 0) return (new Storage()).done(a);
if (n == 1) return (new Storage()).done(b);
return () -> fib(n - 1, b, a + b);
}
}
class Storage {
public <T> TailCall<T> done(final T value) {
return new TailCall<T>() {
@Override
public boolean isComplete() {
return true;
}
@Override
public T result() {
return value;
}
@Override
public TailCall<T> apply() {
throw new Error("not implemented");
}
};
}
}
interface TailCall<T> {
TailCall<T> apply();
default boolean isComplete() {
return false;
}
default T result() {
throw new Error("not implemented");
}
default T invoke() {
return Stream.iterate(this, TailCall::apply)
.filter(TailCall::isComplete)
.findFirst()
.get()
.result();
}
}
爬楼梯
class Solution {
public int climbStairs(int n) {
if (n == 0) return 0;
if(n==1){return 1;}
if(n==2){return 2;}
return Math.toIntExact(new TailCallPower().fib(n,2,3).invoke());
}
}
class TailCallPower{
//Bussiness Logic
public static TailCall<Long> fib(final long n,final long a,final long b) {
if (n == 3) return (new Storage()).done(b);
return () -> fib(n - 1, b, a + b);
}
}
class Storage {
public <T> TailCall<T> done(final T value) {
return new TailCall<T>() {
@Override
public boolean isComplete() {
return true;
}
@Override
public T result() {
return value;
}
@Override
public TailCall<T> apply() {
throw new Error("not implemented");
}
};
}
}
interface TailCall<T> {
TailCall<T> apply();
default boolean isComplete() {
return false;
}
default T result() {
throw new Error("not implemented");
}
default T invoke() {
return Stream.iterate(this, TailCall::apply)
.filter(TailCall::isComplete)
.findFirst()
.get()
.result();
}
}