2012-02-03 8 views
9

Ho una funzione ricorsiva che restituisce tutti i nodi della sottostruttura, dato il nodo radice iniziale.Ricorsione con elementi di ritorno di resa ordine nell'albero

private IEnumerable<Node> getAllNodesRecursively(Node subnode) 
{ 
    foreach (Node node in subnode.Nodes) 
     getAllNodesRecursively(node); 

    yield return subnode; 
} 

Per la seguente struttura ad albero:

A 
| 
+--B 
| 
+--C 
| | 
| +--D 
| 
+--E 

Quando provo a iterare in quanto tale:

foreach (Node n in getAllNodesRecursively(a)) 
{ 
    Console.WriteLine(n); 
} 

la funzione restituisce il solo il Valore.

Desidero utilizzare il rendimento-ritorno con ricorsione e recuperare elementi nel Preordine (A, B, C, D, E in questo esempio).

(Se inserisco il rendimento prima del foreach, il foreach non avverrà mai).

È possibile?

+0

Hai provato che il foreach non viene chiamato se si inserisce il rendimento in anticipo? Immagino che verrà chiamato. – okrumnow

+0

Sì, avevi ragione. Il rendimento restituito non salta il resto del codice. Sembra che sia solo uno zucchero sintattico per consentire il ritorno del valore e mantenere comunque attiva la funzione. Colpa mia. –

risposta

16

Hai provato qualcosa di simile:

private IEnumerable<Node> getAllNodesRecursively(Node subnode) 
{ 
    // Return the parent before its children 
    yield return subnode; 

    foreach (Node node in subnode.Nodes) 
    { 
     foreach(Node n in getAllNodesRecursively(node)) 
     { 
      yield return n; 
     } 
    } 
} 

L'implementazione sta chiamando getAllNodesRecursively in modo ricorsivo, ma ignorando il suo valore di ritorno.

+0

Funziona, ma perché devo ripetere ancora (foreach interno)? Perché il metodo iteratore non consente la ricorsione così com'è? Ho provato con il debugger e ha semplicemente saltato la mia chiamata ricorsiva a meno che non sia usata in una iterazione (come foreach). Perché? –

+0

@ChristianHayter "Non verrà restituito tutto tranne il nodo superiore due volte?" No. – Joe

+1

L'esempio di Joe può essere leggermente semplificato: IEnumerable privato getAllNodesRecursively (radice del nodo) { return return root; foreach (var child in root.Nodes.SelectMany (getAllNodesRecursively)) { yield return child; } } – peter70

3

Sì, è possibile, basta mettere lo yield return prima dello foreach. Stai pensando al comportamento di una normale istruzione return.

+0

Ho modificato il mio post, perché ho sbagliato in quello che verrà mostrato. Viene restituito solo il primo elemento. Se metto il rendimento prima del foreach, succede la stessa cosa. Come mai? –

+0

Hmmm. Non sono nella posizione di provarlo da solo adesso. Hai provato a passare il codice con il debugger? –

+0

Il debugger salta semplicemente la chiamata ricorsiva. È interessante. –

1
 public IEnumerable<int> preOrder(Node root) 
     { 
      if (root == null) 
       yield break; 

      yield return root.val; 

      if (root.left != null) 
       foreach (int i in preOrder(root.left)) 
        yield return i; 

      if (root.right != null) 
       foreach (int i in preOrder(root.right)) 
        yield return i; 
     } 
+0

Sebbene questo snippet di codice possa risolvere la domanda, [compresa una spiegazione] (http://meta.stackexchange.com/questions/114762/explaining-entually-code-based-answers) aiuta davvero a migliorare la qualità del tuo post. Ricorda che stai rispondendo alla domanda per i lettori in futuro, e queste persone potrebbero non conoscere le ragioni del tuo suggerimento sul codice. – lokusking