2010-11-17 22 views
14

¿Es posible utilizar la recursión en un iterador que implemente System.Collections.IEnumerable? Tengo una estructura de árbol declarada más o menos así:Recursividad en el iterador de C#

public class Node 
{ 
    public Node Sibling; 
    public Node Child; 
} 

Me gustaría iterar sobre los nodos de un árbol. Me gustaría hacer algo como esto (pseudocódigo, supongo que esto no se compilará):

public class NodeIterator : System.Collections.IEnumerable 
{ 
    Node m_root; 

    public System.Collections.IEnumerator GetEnumerator() 
    { 
     recursiveYield(m_root); 
    } 

    System.Collections.IEnumeraton recursiveYield(Node node) 
    { 
     yield return node; 
     if (node.Child) 
     { 
      recursiveYield(node.Child); 
     } 
     if (node.Sibling) 
     { 
      recursiveYield(node.Sibling); 
     } 
    } 
} 

¿Es esto de alguna manera posible? Me doy cuenta de que esto se puede resolver sin recursión usando una deque Node en la función GetEnumerator.

+0

debería funcionar. ¿Has probado esto? a los clientes no les debería importar cómo se implementa, solo tiene que poder iterar a través del receptor. Si la recursión simplifica su código, úselo. – Gishu

+1

@Gishu - debería funcionar (aparte de 'IEnumeraton'), pero ignora el valor de retorno de' recursiveYield (node.Child) 'y' recursiveYield (node.Sibling) '. – Kobi

+2

No es exactamente un duplicado, pero [rendimiento de C# del rendimiento anidado en un árbol] (http://stackoverflow.com/questions/1043050/c-performance-of-nested-yield-in-a-tree) muestra algunas soluciones y discute algunas implicaciones de rendimiento. –

Respuesta

19

Sí, todo lo que necesita es repetir el valor de retorno desde el sitio de la llamada. Como tal:

IEnumerable<T> Recursive(Node node) 
{ 
    yield return node; 
    foreach (var siblingNode in Recursive(node.Sibling)) 
    { 
     yield return siblingNode; 
    } 
    foreach (var childNode in Recursive(node.Child)) 
    { 
     yield return childNode; 
    } 
} 

Para el registro, esto no es mejor que usar una cola para lograr, p. Recorrido de primer nivel. El requisito de memoria para algo como esto es idéntico en el peor de los casos.

+0

¿Necesito crear un iterador o sería posible crear una función estática que produzca recursivamente los diferentes valores: 'static IEnumberable Recursive (Node node)' y luego en el cliente 'foreach (nodo var en Recursive (raíz)) '? –

+2

+1 La recursividad es hermosa – sheikhjabootie

+0

Hola John Leidegren, sí, esto es muy bonito, corto y dulce :) Pero ¿estás seguro de que el requisito de memoria será idéntico? Si recuerdo haber verificado correctamente un código similar, ¡no fue en absoluto! New ArrayList o List crea en cada llamada. –

3

No porque la función recursiveYield(Node node) podría devolver una colección y sólo se puede producir un artículo

+0

Si bien es cierto (y desafortunado) no se puede combinar 'return collection' con' yield return item', hay una solución fácil. – Kobi

+0

intentará :)) no estoy seguro de que funcione –

+0

@Kobi: siempre hay soluciones, pero ¿funcionaría el que está en cuestión ??? –