2010-09-02 39 views
7

我有這樣一個類:是否可以寫一個遞歸的IEnumerable <T>

class Spline 
    int ChildrenCount; 
    Spline GetChild (int index) 

class SplineCollection : IEnumerable<Spline> 
    Spline Master 

是否可以寫一個遞歸的IEnumerable的SplineCollection那裏將返回所有的孩子們一個個?

編輯:所以師父是根盒,其子女的層次結構可以是任何深度。

編輯:通過使用名稱框,我想我困惑了一些人。它意味着一個幾何對象,而不是一個容器。因此將其更改爲Spline。

+1

當你寫下「孩子」時,我認爲你的意思是「後代」,因爲讓孩子不需要遞歸。 – 2010-09-02 21:42:35

+0

@工作,是的,你是對的我的意思是後代。只是在我使用的sdk中,它們仍然被稱爲Children,ChildrenRecursive,所以我就是這麼用的。 – 2010-09-02 22:30:34

回答

9

這將對Box'樹'進行深度優先遍歷。然後,您可以在Master框中調用此方法以返回所有遞歸子項。

public class Box 
{ 
    // ... 

    public IEnumerable<Box> GetBoxes() 
    { 
     yield return this; 

     for (int i=0; i<box.ChildrenCount; i++) 
     { 
      foreach (Box child in box.GetChild(i).GetBoxes()) 
      { 
       yield return child; 
      } 
     } 
    } 
} 
+0

謝謝,但這會通過每個box.GetChild(我)的兒童? – 2010-09-02 21:22:56

+0

啊,現在它是有道理的。編輯答案。 – thecoop 2010-09-02 22:14:30

1
class Box 
{ 
    int ChildrenCount; 
    Box GetChild (int index){/* some implementation*/} 
    public IEnumerable<Box> Children 
    { 
    get 
    { 
     for(int i = 0; i != ChildrenCount; ++i) 
     yield return GetChild(i); 
    } 
    } 
    public IEnumerable<Box> Descendants 
    { 
    get 
    { 
     foreach(Box child in Children) 
     { 
     yield return child; 
     foreach(Box desc in child.Descendants) 
      yield return desc; 
     } 
    } 
    } 
} 

您可以從BoxCollection調用此方法,但由於框已經框的集合,我沒有看到BoxCollection的目的是在這裏。就此而言,使用Box實現IEnumerable<Box>或其後代之一(ICollection<Box>IList<Box>)可能會提高實用性。

也可以用迭代而不是遞歸的方式來完成它,它有時會有更好的性能(幾乎任何時候編譯器都不會將遞歸轉化爲interation),但遞歸更具可讀性,通常超過性能就夠了。

-1

當然。你甚至不真的需要一個BoxContainer,因爲盒子,它的名字,存在作爲容器:

public class Box 
{ 
    private List<Box> myBoxes; 

    public IEnumerable<Box> GetAllBoxes() 
    { 
     yield return this; 
     foreach (var box in myBoxes) 
     { 
      var enumerator = box.GetAllBoxes().GetEnumerator(); 
      while(enumerator.MoveNext()) 
       yield return enumerator.Current; 
     } 
    } 
} 

如果一箱箱持有B和C,盒B保持着盒d和E,以及箱C召開框F,可枚舉出來A,B,D,E,C,F。

+0

您的'foreach'有效地導致返回類型爲'IEnumerable >',它與聲明的返回類型不匹配。我不相信這會在C#中編譯。在F#中,你可以將第二個yield轉換爲yield,並且它可以正常工作。 – 2010-09-02 22:13:10

+0

你完全正確。快速編輯來遍歷子Box所返回的IEnumerable。 – KeithS 2010-09-02 22:49:49

1

是的,但你必須枚舉遞歸結果。你不能只返回它,因爲類型不匹配。

IEnumerable<int> Triangle(int n) { 
    yield return n; 
    if (n > 0) 
     foreach (var e in Triangle(n - 1)) 
      yield return e; 
} 
10

我會去手動維護堆棧,而不是依靠這裏的調用堆棧。原因是因爲如果您通過遞歸調用獲取後代的方法使用調用堆棧,則必須爲每個Spline訪問創建一個新的IEnumerable<Spline>。那將是無效的。你可以通過使用自己的堆棧來顯着改善遍歷。

public IEnumerable<Spline> Descendants 
{ 
    get 
    { 
     // This performs a simple iterative preorder traversal. 
     var stack = new Stack<Spline>(new Spline[] { this }); 
     while (stack.Count > 0) 
     { 
      Spline current = stack.Pop(); 
      yield return current; 
      for (int i = current.ChildrenCount - 1; i >= 0; i--) 
      { 
       stack.Push(current.GetChild(i)); 
      } 
     } 
    } 
} 
+2

我希望我能投票10次。這比*遞歸解決方案*效率更高,只需要更多的思考。事實上,你可以把它推廣到一個'Flatten'函數來簡化任何遞歸迭代器。 – 2012-09-19 21:32:28

相關問題