递归是一种“分而治之”的风格,它在变小时分裂(树数据结构),如果发现违规,我希望它完全破坏,意味着打破所有递归路径,并返回真正。这可能吗?
答案 0 :(得分:36)
无论你做什么,你都必须解开堆栈。这留下了两个选择:
如果您希望死亡的情况很少,这可能是抛出异常可能是一个可行选择的情况之一。在每个人都对此嗤之以鼻之前,请记住,最重要的编程规则之一是知道何时违反规则。
事实证明,我今天花了从谷歌代码评估zxing库。它们实际上对许多控制结构使用异常抛出。当我看到它时,我的第一印象是恐怖。他们实际上是用不同的参数调用方法数万次,直到方法没有抛出异常。
这当然看起来像是一个性能问题,因此我做了一些调整,将事情改为使用魔法返回值。你知道吗?在调试器中运行时,代码速度提高了40%。但是当我切换到非调试时,代码的速度提高了不到1%。
对于在这种情况下使用流控制异常的决定,我仍然不是很疯狂(我的意思是,异常会被抛出所有时间)。但鉴于几乎无法衡量的性能差异,重新实施它当然不值得我花时间。
如果触发迭代死亡的条件不是算法的基本部分,则使用异常可能会使代码更清晰。对我来说,我做出这个决定的地方是整个递归是否需要解开,然后我会使用异常。如果只需要解开部分递归,请使用魔法返回值。
答案 1 :(得分:15)
您可以返回错误代码,或修改一些全局变量,以便每个递归实例都知道“自杀”。
某种东西。
int foo(bar){
int to_the_next;
if (go_recursive){
to_the_next = foo(whisky_bar);
if (to_the_next ==DIE) return DIE;
}
if (something_unexpected_happened) return DIE;
process;//may include some other recursive calls, etc etc
}
答案 2 :(得分:5)
如果它是执行递归的单个线程,则可能抛出异常。 虽然有点丑陋 - 有点使用异常作为goto。
boolean myPublicWrapperMethod(...) {
try {
return myPrivateRecursiveFunction(...);
} catch (MySpecificException e) {
return true;
}
}
更好的方法是消除递归,并使用一个Stack集合,该集合包含一个表示本来是递归状态的类,在循环中迭代,并在需要时返回true。
答案 3 :(得分:4)
我建议进行异常处理。这表明,由于某些违规(或其他异常),递归被中止:
public void outer() {
try {
int i = recurse(0);
} catch (OuchException ouch) {
// handle the exception here
}
}
private int recurse(int i) throws OuchException {
// for tree-like structures
if (thisIsALeaf) {
return i;
}
// the violation test
if (itHurts)
throw new OuchException("That really hurts");
// we also can encapsulate other exceptions
try {
someMethod();
} catch (Exception oops) {
throw new OuchException(oops);
}
// recurse
return recurse(i++);
}
当然,这违反了堕胎时返回“真实”的初始要求。但我更喜欢清楚地分离返回值和异常行为的通知。
答案 4 :(得分:1)
您可以通过存储跟踪递归是否应该中断的变量来执行类似的操作。不幸的是,你必须在每次递归时检查它,但你可以这样做。
答案 5 :(得分:1)
你要问的是递归的定义。
在某些时候,所有递归路径都应该中断。否则它将是无限递归并且stack overflow exception发生。
所以你应该设计这样的递归函数。示例binary search in a sorted array:
BinarySearch(A[0..N-1], value, low, high) {
if (high < low)
return -1 // not found
mid = low + ((high - low) / 2) // Note: not (low + high) / 2 !!
if (A[mid] > value)
return BinarySearch(A, value, low, mid-1)
else if (A[mid] < value)
return BinarySearch(A, value, mid+1, high)
else
return mid // found
}
答案 6 :(得分:0)
除非并行计算递归调用,否则您可能只需要添加一些逻辑来检查第一个递归调用的值,然后再进行第二次(以及后续的,如果不是二叉树结构)递归调用。
public abstract class Tree {
protected abstract boolean headIsViolation();
protected abstract boolean isLeaf();
public Tree getLeft();
public Tree getRight();
// Recursive
public boolean checkViolation() {
if(this.isLeaf()) {
return this.headIsViolation();
}
// If needed, you could pass some sort of 'calculation state'
// object through the recursive calls and evaluate the violation
// through that object if the current node is insufficient
if(this.headIsViolation()) {
// Terminate the recursion
return true;
}
// Fortunately, Java short-circuits ||
// So if the left child is in violation, the right child will
// not even be checked
return this.getLeft().checkViolation() || this.getRight().checkViolation();
}
}
答案 7 :(得分:0)
我能够使用全局变量移出所有递归调用。
boolean skipRecursivePaths = false;
private callAgain(){
if(callAgain){
if(getOutCompletely){
skipRecursivePaths = true;
}
if(skipRecursivePaths){
return;
}
}
答案 8 :(得分:0)
遇到错误时退出递归循环的最佳方法是抛出运行时异常。
getMemoryInfo.availMem().
当然,这会杀死你的程序,但你应该使用范围检查和算法分析来确保你的递归不会抛出这样的异常。您可能想要打破递归算法的一个原因是您的内存不足。在这里,可以确定算法将在堆栈上使用多少内存。如果您正在编写Java,请将该计算与
进行比较public long fact(int n)
{
long val = 1;
for (int i = 1, i<=n,i++)
return val *= fact(i);
return val;
}
假设您正在使用递归来查找n!。你的功能看起来像这样:
.then((a) => {
this.b(a);
})
在运行它之前,请检查内存中是否有(长的8个字节的字节数)* n个字节来保存整个堆栈。基本上,通过在递归方法/函数之前进行范围和错误检查,您不需要突破。但是,根据您的算法,您可能需要向整个堆栈发出信号,表示您很高兴。如果是这样的话,汤姆的回答是有效的。
答案 9 :(得分:0)
Maybe you want to avoid recursion and replace it with a stack. This gives you the control to break out of the operation, while being able to do something that resembles a recursive operation. In fact you just emulate recursion by yourself.
I found a nice explanation here: http://haacked.com/archive/2007/03/04/Replacing_Recursion_With_a_Stack.aspx/
答案 10 :(得分:-1)
better to have a Boolean array
that way there is no global state
if(stop[0]) return;