2010-07-13 85 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); 
}