2010-09-02 5 views
7

Ho una classe come:E 'possibile scrivere un ricorsiva IEnumerable <T>

class Spline 
    int ChildrenCount; 
    Spline GetChild (int index) 

class SplineCollection : IEnumerable<Spline> 
    Spline Master 

E' possibile scrivere un IEnumerable ricorsivo per l'SplineCollection in cui si tornerà tutti i bambini uno per uno?

MODIFICA: così Master è la casella radice e la gerarchia dei suoi figli può essere qualsiasi profondità.

MODIFICA: utilizzando il nome Box, penso di aver confuso alcune persone. È pensato per essere un oggetto geometrico, non un contenitore. Quindi cambiandolo in Spline.

+1

Secondo me, si intende "discendenti" quando si scrivono "bambini", poiché ottenere i bambini non richiede alcuna ricorsione. –

+0

@Job, sì hai ragione intendevo discendenti. E 'solo che nel sdk che sto usando, sono ancora definiti Children, Children Recursive, quindi è per questo che l'ho usato. –

risposta

9

Questo eseguirà un attraversamento in profondità dell'albero Box. È quindi possibile chiamare questo metodo sulla casella Master per restituire tutti i figli ricorsivi.

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

Grazie, ma questo passerà attraverso i Figli di ogni box.GetChild (i) anche tu? –

+0

Ah, ora ha senso. Modificato la risposta. – thecoop

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; 
     } 
    } 
    } 
} 

È possibile chiamare questo da BoxCollection, ma dal momento Box è già una collezione di scatole, non vedo quale scopo di BoxCollection è qui. Del resto, avere l'implementazione Box IEnumerable<Box> o uno dei suoi discendenti (ICollection<Box>, IList<Box>) probabilmente migliorerebbe l'utilità.

È anche possibile farlo in modo iterativo piuttosto che ricorsivo, che a volte ha prestazioni migliori (praticamente ogni volta che il compilatore non trasforma la ricorsione in interation comunque), ma è ricorsivo più leggibile e normalmente più che performante.

-1

Sicuro. Non è ancora veramente bisogno di un BoxContainer, dal box, con il suo nome, esiste come un contenitore:

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; 
     } 
    } 
} 

Se Box A ha tenuto Scatole B e C, scatola B tenuto scatole D ed E, e la casella C tenuto casella F, l'enumerabile verrebbe fuori A, B, D, E, C, F.

+0

Il tuo 'foreach' risulta in un tipo di ritorno di' IEnumerable > 'che non corrisponde al tipo di reso dichiarato. Non credo che questo si possa compilare in C#. In F # potresti cambiare il secondo 'yield' in un' yield! 'E funzionerebbe perfettamente. –

+0

Hai esattamente ragione.Modifica rapida per scorrere l'oggetto IEnumerable restituito dalla casella secondaria. – KeithS

1

Sì, ma è necessario enumerare il risultato ricorsivo. Non puoi semplicemente restituirlo, perché il tipo non corrisponde.

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

Vorrei andare mantenendo manualmente uno stack invece di fare affidamento sullo stack di chiamate qui. Il motivo è perché dovrebbe essere creato un nuovo IEnumerable<Spline> per ogni Spline visitato se si utilizza lo stack di chiamate chiamando in modo ricorsivo il metodo che ottiene i discendenti. Sarebbe inefficiente. Puoi migliorare significativamente l'attraversamento usando il tuo stack personale.

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

Vorrei poter votare questo 10 volte. Questo è * così * molto più efficiente di una soluzione ricorsiva e richiede solo pochi secondi di riflessione. In effetti, è possibile generalizzare questa funzione in una funzione "Appiattisci" per semplificare qualsiasi iteratore ricorsivo. –

Problemi correlati