2010-09-02 18 views
7

tengo una clase como:¿Es posible escribir una recursiva IEnumerable <T>

class Spline 
    int ChildrenCount; 
    Spline GetChild (int index) 

class SplineCollection : IEnumerable<Spline> 
    Spline Master 

¿Es posible escribir un IEnumerable recursiva para el SplineCollection donde va a devolver todos los niños uno por uno?

EDITAR: So Master es el Root Box, y la jerarquía de sus hijos puede ser de cualquier profundidad.

EDITAR: Al usar el cuadro Nombre, creo confundir a algunas personas. Se supone que es un objeto geométrico, no un contenedor. Entonces cambiándolo a Spline.

+1

Supongo que significa "descendientes" cuando escribe "niños", ya que obtener los hijos no requiere recursión. –

+0

@Job, sí, tienes razón, quise decir descendientes. Es solo que en el SDK que estoy usando, todavía se los llama Children, ChildrenRecursive, así que es por eso que acabo de usar eso. –

Respuesta

9

Esto hará un recorrido en profundidad del Box 'árbol'. A continuación, puede llamar a este método en el cuadro Master para devolver todos los secundarios recursivos.

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

Gracias, pero ¿esto pasará por los hijos de cada caja. GetChild (i) también? –

+0

Ah, ahora tiene sentido. Editado la respuesta. – 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; 
     } 
    } 
    } 
} 

Puede llamar esto desde BoxCollection, pero dado que la caja ya es una colección de cajas, que no ven lo que el propósito de BoxCollection está aquí. Para el caso, tener Box implemetar IEnumerable<Box> o uno de sus descendientes (ICollection<Box>, IList<Box>) probablemente mejoraría la utilidad.

También es posible hacerlo de forma iterativa en lugar de recurrente, que a veces tiene un mejor rendimiento (casi siempre que el compilador no convierta la recursión en interation de todos modos), pero recursivo es más legible y normalmente más que lo suficientemente eficiente.

-1

Sure. Que ni siquiera realmente necesita un BoxContainer, ya que la caja, por su nombre, existe como un contenedor:

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

Si Caja A celebró cajas B y C, caja B celebró casillas D y E, y la caja C celebrada cuadro F, el enumerable saldría A, B, D, E, C, F.

+0

Su 'foreach' resulta efectivamente en un tipo de devolución de 'IEnumerable >' que no coincide con el tipo de devolución declarada. No creo que esto se recopile en C#. En F #, podría cambiar el segundo "rendimiento" en un "rendimiento!" Y funcionaría perfectamente. –

+0

Tienes razón.Edición rápida para iterar a través de IEnumerable devuelto por el cuadro secundario. – KeithS

1

Sí, pero debe enumerar el resultado recursivo. No puede simplemente devolverlo, porque el tipo no coincide.

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

Me gustaría mantener manualmente una pila en lugar de confiar en la pila de llamadas aquí. El motivo se debe a que debería crearse un nuevo IEnumerable<Spline> para cada Spline visitado si utilizaba la pila de llamadas llamando recursivamente al método que obtiene los descendientes. Eso sería ineficiente. Puede mejorar significativamente el recorrido utilizando su propia pila.

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

Ojalá pudiera votar esto 10 veces. Esto es * por lo tanto * mucho más eficiente que una solución recursiva, y solo requiere unos pocos segundos más de reflexión. De hecho, podrías generalizar esto en una función 'Flatten' para simplificar cualquier iterador recursivo. –

Cuestiones relacionadas