2010-11-17 19 views
14

E 'possibile utilizzare la ricorsione in un iteratore attuazione System.Collections.IEnumerable? Ho una struttura ad albero dichiarata all'incirca in questo modo:ricorsione in C# iteratore

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

Vorrei eseguire un'iterazione sui nodi di un albero. Mi piacerebbe fare qualcosa di simile (pseudocodice, credo che questo non verrà compilato):

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

È questo in qualche modo possibile? Mi rendo conto che questo può essere risolto senza ricorsione utilizzando una deque Node nella funzione GetEnumerator.

+0

dovrebbe funzionare. Hai provato questo? i clienti non dovrebbero preoccuparsi di come è implementato - deve solo essere in grado di scorrere il ricevitore. Se la ricorsione rende il tuo codice più semplice, usalo. – Gishu

+1

@Gishu - dovrebbe funzionare (a parte 'IEnumeraton'), ma si ignora il valore di ritorno del' recursiveYield (node.Child) 'e' recursiveYield (node.Sibling) '. – Kobi

+2

Non esattamente un duplicato, ma [C# prestazioni del rendimento annidato in un albero] (http://stackoverflow.com/questions/1043050/c-performance-of-nested-yield-in-a-tree) mostra alcune soluzioni e discute alcune implicazioni sulle prestazioni. –

risposta

19

Sì, tutto ciò che serve è quello di iterare il valore restituito dal sito chiamata. Così:

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

Per la registrazione, questo non è migliore di usare una coda per raggiungere, ad es. ampiezza trasversale. Il requisito di memoria per qualcosa di simile è identico nel peggiore dei casi.

+0

ho bisogno di creare un iteratore affatto o sarebbe possibile creare una funzione statica che produce ricorsivamente i diversi valori: 'statica IEnumberable ricorsiva (nodo Node)' e poi al cliente 'foreach (var nodo in ricorsivo (root)) '? –

+2

+1 La ricorsione è bella – sheikhjabootie

+0

Ciao John Leidegren, sì questo è molto bello - breve e dolce :) Ma sei sicuro che il requisito di memoria sarà identico? Se ricordo di aver controllato correttamente un codice simile, non lo era affatto! New ArrayList o List create su ogni chiamata. –

3

No perché la funzione recursiveYield(Node node) sarebbe restituire un insieme e si può produrre solo un elemento

+0

Mentre è vero (e sfortunato) non è possibile combinare la "raccolta di ritorno" con "elemento di resa", c'è una soluzione facile. – Kobi

+0

proverà :)) non è sicuro che funzionerebbe –

+0

@Kobi: c'è sempre un workaround ma quello in questione funzionerebbe ???? –