Java 如何在递归中使用索引变量?
我希望在递归中使用索引变量,而不在调用函数时将其作为参数发送。但是,如果我在开始时重置它(例如I=0),它将在每次运行时重置。我想把它用作计数器(计算函数运行的次数)。最自然的做法是使用一个辅助参数,正如您在文章中所描述的,尤其是当它用于确定何时停止递归时。如果依赖于成员变量,则可以使用helper方法在调用递归方法之前重置couter,并在需要调用递归函数时调用helper方法 如果您喜欢使用单一方法,则必须执行以下操作:Java 如何在递归中使用索引变量?,java,variables,recursion,indexing,Java,Variables,Recursion,Indexing,我希望在递归中使用索引变量,而不在调用函数时将其作为参数发送。但是,如果我在开始时重置它(例如I=0),它将在每次运行时重置。我想把它用作计数器(计算函数运行的次数)。最自然的做法是使用一个辅助参数,正如您在文章中所描述的,尤其是当它用于确定何时停止递归时。如果依赖于成员变量,则可以使用helper方法在调用递归方法之前重置couter,并在需要调用递归函数时调用helper方法 如果您喜欢使用单一方法,则必须执行以下操作: static int counter; public void st
static int counter;
public void start() {
counter = 0;
recurse();
}
public void recurse() {
counter += 1;
if (whatever) { recurse(); }
}
创建名为insideMethod
的成员变量,该变量默认设置为false。调用该方法时,它将检查该变量。如果为false,则是第一次调用,计数器应重置,否则计数器应仅递增。之后,insideMethod
被设置为true。返回时,insideMethod
应设置回false,前提是该调用将其设置为true
记住将insideMethod
和索引变量设置为ThreadLocal
伪代码:
ThreadLocal<Boolean> insideMethod = false
ThreadLocal<Integer> index = 0
....
void recMethod(args) {
boolean topCall = (insideMethod == false)
insideMethod = true
if (topCall)
index = 0
else
index++
// Body of the method...
if (topCall)
insideMethod = false
}
ThreadLocal insideMethod=false
线程本地索引=0
....
void recMethod(args){
布尔topCall=(insideMethod==false)
insideMethod=true
如果(topCall)
索引=0
其他的
索引++
//方法的主体。。。
如果(topCall)
insideMethod=false
}
首先,您显然只需要初始化它一次。递归中的一种常见模式是:
public void run(int opt) {
run_helper(opt, 0);
}
private void run(int opt, int depth) {
if (whatever) { run(opt, depth + 1); }
}
其中外部方法只执行一些初始化
您经常会看到的一个“解决方案”(例如,在问题的第一条评论中)是使用静态变量。这种方法是一种糟糕的风格,一旦添加多线程(例如,通过制作UI版本,或在多线程web服务器中运行),就会导致程序以各种奇怪的方式失败。最糟糕的是,它可能在一开始看起来是有效的,只有在有很多用户的情况下才开始微妙地行为不端所以除了常数之外,任何东西都要远离“静态”强>
对于“静态”,它通常如下所示:
static int counter;
public void start() {
counter = 0;
recurse();
}
public void recurse() {
counter += 1;
if (whatever) { recurse(); }
}
现在假设两个用户同时调用start
它们将覆盖彼此计数器因为静态意味着它在线程和用户之间共享
下面是一个非常简单易懂的解决方案:
class MyTask {
int counter = 0;
public void recurse() {
counter++;
if (whatever) { recurse(); }
}
public int getIterations() {
return counter;
}
}
public void run() {
MyTask task = new MyTask();
task.run();
System.out.println("Task took "+task.getIterations()+" itertions.");
}
然后创建一个任务,运行它,并在最后检索计数器。干净、简单、高效、可靠如果您有多个线程/用户,每个线程/用户将有一个单独的MyTask对象,您不会遇到任何问题。
此外,您还可以添加其他统计信息,它们都被清晰地包装在task对象中。“每次迭代的平均时间”?“平均递归深度”?没问题。任务对象也可用于存储结果
此处建议使用
ThreadLocal
。我不同意这一点。它根本不提供任务对象的好处。只要试着用ThreadLocal
和任务对象来实现它,您就会看到区别。另外,ThreadLocal
在经验上比访问堆值慢10倍(请参阅)。对于int
而言,情况可能更糟。因此,永远不要在性能关键的代码路径中调用ThreadLocal#get
。如果您打算使用ThreadLocal
,请在此代码路径之外使用它,并使用局部变量(或任务对象)将“局部静态”变量馈送到关键代码路径中。您应该使用两种方法将其分离:一种是公共方法,用于启动递归迭代并将计数器初始化为零;另一种是私有方法,这就是进行递归调用的地方。这样,每次调用public方法时,计数器都会初始化。它应该是这样的(在java中):
考虑到索引被用作停止条件,我将使用一种简单的基于参数的方法。JavaScript实现:
var recurse = function() {
recurseHelper(0);
};
var recurseHelper = function(iteration) {
// run for 100 iterations,
if (iterationCount > 100)
return;
// do stuff...
recurseHelper(iteration + 1);
}
然而,如果您只想调用一个函数若干次,为什么要特别使用递归呢?你可以用一个循环。同样在JavaScript中:
for (var i = 0; i < 100; i++) {
// do stuff...
}
for(变量i=0;i<100;i++){
//做些事情。。。
}
根据递归算法的复杂性,编译器展开循环比递归构造更有趣。可以使用属性,但为什么?为什么不将该值作为参数传递
public class Empty {
private int steps = 0;
public static void main (String args [])
{
Empty e = new Empty ();
System.out.println (e.reverse ("This is a test"));
System.out.println ("steps: " + e.steps);
}
public String reverse (String s) {
++steps;
if (s.length () < 2)
return s;
else return reverse (s.substring (1)) + s.charAt (0);
}
}
公共类为空{
私有整数步长=0;
公共静态void main(字符串参数[])
{
Empty e=新的Empty();
System.out.println(例如,反向(“这是一个测试”);
System.out.println(“步骤:+e.steps”);
}
公共字符串反转(字符串s){
++步骤;
如果(s.length()<2)
返回s;
否则返回反向(s.子字符串(1))+s.字符(0);
}
}
从我得到的注释中,您可以将其用作计数器,以检测何时结束递归。那看起来没什么用。难道您没有一个可以从参数派生的条件,比如list.length()之类的吗
当然,两次调用该方法将进一步增加计数器。如果两个线程不同时使用它,您可以重置它,并且内部对象中的包装方法可能有助于防止在不先重置计数器的情况下调用它
但这比在柜台上转悠要简单得多
参数是计算调用次数的更干净的解决方案
如果您想在调试时防止堆栈溢出,一个有趣的替代方法是调用随机化器,并在10000个案例中的1个案例中返回 只要你答应在打电话给t时重置它