2010-02-11 2 views
3
public static bool AllNodesChecked(TreeNodeCollection nodes)   
{ 
    foreach (TreeNode node in nodes) 
    { 
     if (!node.Checked) 
     { 
      return false; 
     } 
     AllNodesChecked(node.Nodes); 
    } 
    return true; 
} 

arbre test estPourquoi cette récursivité ne fonctionne-t-elle pas en C#?

A1(checked) -> B1(unchecked) 
A2(checked) 
A3(checked) 

mais il ne retourne pas quand il frappe noeud B1.

EDIT: Merci à tous d'avoir aidé mon cerveau fatigué. La récursivité ne devrait être tentée que tôt dans la journée après une douche froide.

Répondre

20

Vous ignorez la valeur de retour de AllNodesChecked dans l'appel récursif:

public static bool AllNodesChecked(TreeNodeCollection nodes)   
{ 
    foreach (TreeNode node in nodes) 
     if (!node.Checked || !AllNodesChecked(node.Nodes)) 
      return false; 
    return true; 
} 

La déclaration return renvoie uniquement à partir de la méthode actuelle dans la pile d'appel à l'appelant immédiat. Il ne revient pas soudainement de tous les autres appels ci-dessus dans la pile d'appels.

+0

Doh - merci! –

+3

+1 pour ne pas comparer un booléen à un littéral! –

+0

Oui, je l'ai remarqué aussi. Je le blâme sur le fait qu'il ne s'appelle pas IsChecked comme il se doit. :-) –

8

Change:

AllNodesChecked(node.Nodes); 

Pour:

if(!AllNodesChecked(node.Nodes)) 
    return false; 
2

Essayez ceci:

public static bool AllNodesChecked(TreeNodeCollection nodes)   
{ 
    foreach (TreeNode node in nodes) 
    { 
     if (node.Checked == false || !AllNodesChecked(node.Nodes)) 
     { 
      return false; 
     } 
    } 
    return true; 
} 
3

Vous n'êtes pas évaluer le résultat de l'appel récursif pour vérifier les nœuds enfants.

7

Je prendrais une approche légèrement différente ici. Ce que je ferais, c'est que j'écrirais d'abord du code qui transforme votre arbre (ce que je suppose être un arbre, pas un graphe arbitraire) en une séquence de nœuds. Quelque chose comme:

static IEnumerable<Node> AllNodes(this Node node) 
{ 
    var stack = new Stack<Node>(); 
    stack.Push(node); 
    while(stack.Count > 0) 
    { 
     var current = stack.Pop(); 
     yield return current; 
     foreach(var child in current.Nodes) 
      stack.Push(child); 
    } 
} 

et maintenant vous pouvez utiliser des opérateurs de séquence:

bool allChecked = root.AllNodes().All(x=>x.Checked); 

Pas de récursion, pas de problème.

+3

Je pense que la récursion est la manière la plus naturelle et la plus lisible de résoudre ce genre de problèmes "d'arbre" (à moins que l'arbre ne soit supposé être plus grand que la pile d'appels ne peut l'accueillir). –

+0

Merci Eric! Cependant toujours sur 2.0 au travail. Je sais que vous pouvez pirater les méthodes d'extension, mais ne voulez pas prendre le risque. –

+2

@Mehrdad: Je suis d'accord, il est plus élégant d'utiliser la récursivité sur une structure de données récursive. Cependant, (1) manque de récursion de queue, (2) échec catastrophique quand la pile est soufflé, (3) les blocs d'itérateur récursifs sont inefficaces, (4) les opérateurs de séquence basculent. Ces points me poussent vers des solutions itératives qui réinterprètent les structures de données récursives comme des séquences non structurées. –

-1

Je dois ajouter mes deux cents .... Apprenez à la programmation fonctionnelle à mon humble avis.

public static bool AllNodesChecked(TreeNodeCollection nodes) 
{ 
    return nodes.All(i => i.Checked && AllNodesChecked(i.Nodes)); 
} 
Questions connexes