2010-07-13 81 views
6

我有以下递归代码,我得到一个stackoverflow异常。我找不出根本原因,因为一旦我得到异常,我没有在Visual Studio中获得完整的调用堆栈。我怎么能抓住递归代码上的stackoverflow异常的根

这个想法是有组织团队成为更大的“主”团队。

有没有人看到下面的代码可能是罪魁祸首的缺陷?

private Unit GetUnit(Unit organisationalUnit) 
    { 
     if (organisationalUnit.IsMainUnit) 
     { 
      return organisationalUnit;     
     } 

     if (organisationalUnit.Parent == null) 
      return null; 

     return GetUnit(organisationalUnit.Parent); 
    } 

回答

4

根是否总是有Parent == null? 试过检查

if (organisationalUnit.Parent == organisationalUnit) 
    return null; 

+0

所以这是一个数据问题,一个单位的父母被设置为自己,但你的代码建议检查这个问题解决了我的问题。 。 – leora 2010-07-13 12:30:14

+0

@ooo我很高兴。错误重演。像历史一样:-) – Mau 2010-07-13 12:41:53

+0

这不会涵盖所有情况,有些周期可能会更深一些,比如'Unit'是它自己的祖父母。 – 2010-07-13 12:56:35

1

是否有任何理由不将它重新编码为迭代?这样,为了捕获不良(循环)数据,对组织树的深度设置硬限制会更容易。

递归是有趣的,尾部优化甚至可以使其效率,但在这里它似乎像一个小问题的大锤子。

+0

在这种情况下,这个问题看起来可能与数据。 – 2010-07-13 12:35:32

+0

确实。但通过迭代解决方案,您可以更轻松地检查更长的周期(例如,将已遇到的节点明确添加到列表或集合中)。 – 2010-07-14 07:49:05

5
  1. 你可能有一个单位是它自己的父母,或者除非你有一个父母的周期(例如A-B-C-A)。也就是说,问题可能与数据有关,而不是与代码本身有关。
  2. 确认IsMainUnitParent的获得者不要拨打GetUnit
1

我对视觉工作室了解不多。 但是你应该检查重复。例如

private Unit GetUnit(Unit organisationalUnit) 
{ 
     GetUnit(organisationalUnit, new vector()); 
} 

private Unit GetUnit(Unit organisationalUnit,vector x) 
{ 
    if (organisationalUnit.IsMainUnit) 
    { 
     return organisationalUnit;     
    } 

    if (organisationalUnit.Parent == null) 
     return null; 

    x.add(this); 
    if(x.contains(organisationalUnit.Parent)) throw new Exception("recurrent parent"); 
    return GetUnit(organisationalUnit.Parent,x); 
} 
0

该代码看起来不错,也许你在单元树中有一些封闭的周期?尝试使用organisationalUnit.GetHashCode值添加跟踪行,跟踪输出不受限制,并且可以帮助检测堆栈溢出原因。

0

我想你可以通过沿

while(organisationalUnit!=null && !organisationalUnit.IsMainUnit) 
    organisationalUnit=organisationalUnit.Parent; 

return organisationalUnit; 

我希望帮助行重写这避免递归。

编辑:我只是意识到,如果你有某种循环依赖,这仍然会失败。

1

这样做的一种方法是减小堆栈大小,以便可以尽早崩溃。

您可以通过在程序开始时浪费帧,即获得如下堆栈跟踪:f_n,f_(n-1),...,f_1,浪费,浪费......,废物,如(在C伪代码中)

int wasted = 1; (if(n> 0)waste(n-1,f)else f(); if(n> 0) 浪费+ = 1; }

main(){waste(N,mainprime); }

其中mainprime是您的旧主,而N大到足以达到您想要的f_1。

2

你可以试试这个来更好地进行调试。它不会影响您的生产代码。

using System.Diagnostics; 

private Unit GetUnit(Unit organisationalUnit, int depth) 
{ 
    debug.assert(depth < 10, "Reached an unexpected high recursion depth"); 

    if (organisationalUnit.IsMainUnit) 
    { 
     return organisationalUnit;     
    } 

    if (organisationalUnit.Parent == null) 
     return null; 

    return GetUnit(organisationalUnit.Parent, depth + 1); 
} 

private Unit GetUnit(Unit organisationalUnit) 
{ 
    return GetUnit(organisationalUnit.Parent, 0); 
} 

关于第二个想法......

这mostlikely你有一个循环引用的地方。

A.parent = B; 
B.parent = C; 
C.parent = A; 

您可以尝试传递一组以前访问过的节点,并检查您是否曾经访问过此节点。

递归的事情是,你必须确保它会结束和一个未经检查的循环引用的情况下它不会结束。

0

你可能在你的图中有一个循环(见,它不是一棵树)。

你可以用这样的代码来检测它:

private Unit GetUnit(Unit organisationalUnit) { 
    return GetUnit(organisationalUnit, new HashSet<Unit>()); 
} 

private Unit GetUnit(Unit organisationalUnit, HashSet<Unit> visited) { 
    if (visited.Contains(organisationalUnit)) { 
    throw new Exception("Cycle detected!"); // or just return null if you prefer 
    } 
    visited.Add(organisationalUnit); 

    if (organisationalUnit.IsMainUnit) { 
    return organisationalUnit; 
    } 

    if (organisationalUnit.Parent == null) 
    return null; 

    return GetUnit(organisationalUnit.Parent, visited); 
}