正如我在评论中发表的那样,这基本上是一个众所周知的函数编程功能“折叠”.
什么是折叠?它被称为fold,因为它使用一些基值和一些函数“折叠”给定的数据结构.在您的情况下,要折叠的数据结构是队列.基值是foldIt(u)的第一个参数,bidi函数是告诉你如何折叠它的函数.它使用3种类型进行推广,因为它包含2种类型并计算第3种类型的结果.
好的,什么?折叠的基本例子是:
你= 1;
队列=(2,3,4)
bidi(t,u)= return(t u)
这里首先foldIt(u,queue,bidi)将添加1 2 = 3,然后调用foldIt(3,(3,4),bidi).因此,您可以看到下一步的基值是上一步的bidi的结果.它继续,直到有折叠元素并返回累积(折叠)值.
问题:现在问题是有人试图在JVM上以功能方式实现它,它不完全支持函数式编程(即使在Java8中).好吧,JVM确实支持它(例如Scala这样做),但Java不支持它(原因不一定好).
因为return语句是对foldIt()方法的调用,所以这不再称为尾递归.尾递归很好,因为你不需要为每个新调用都有一个新的堆栈帧,你可以重用当前的堆栈帧,因为在递归过程中不存在必须持久化的局部变量.
不幸的是,Java不支持尾调用优化,它会为每次调用foldIt()创建一个新的堆栈帧.
这个问题的解决方案已经由@Tassos Bassoukos发布了,您只需要通过迭代将递归调用替换为foldIt().通常怎么做?基本上,当您使用递归(或任何方法调用)时,计算机所做的是它为每个调用创建一个新的堆栈帧,其中包含有关它的信息(局部变量,一些指针等)并将其推送到当前的执行堆栈.因此,为了克服这个问题,您可以进行迭代并将所需的值推送到您自己的堆栈上,这可能会为您节省一些空间(您不需要存储计算机所需的指针,以了解它需要从递归中返回的内存中的哪个位置打电话等).在这种情况下(尾递归)它甚至更好,因为你没有局部变量,你可以跳过堆栈!像这样的东西:
public class CommonFolder implements FolderMaster{
public U foldIt(U u, Queue ts, FunctionBi bidi){
if(u == null || ts == null || bidi == null)
throw new IllegalArgumentException();
while (!ts.isEmpty()) {
u = bidi.applyIt(ts.poll(), u);
}
return u;
}
}
在这里,您不会以递归方式调用任何内容,因此您不会有太多新的堆栈帧(对于isEmpty()和applyIt()只有一个恒定的量)并且没有堆栈溢出.