8
votes

Est-il possible d'écrire un iNeumable reconsive

J'ai une classe comme:

class Spline
    int ChildrenCount;
    Spline GetChild (int index)

class SplineCollection : IEnumerable<Spline>
    Spline Master


2 commentaires

Je suppose que vous voulez dire "descendants" lorsque vous écrivez des "enfants", car les enfants ne nécessitent aucune récursion.


@Job, oui vous avez raison, je voulais dire des descendants. C'est juste que dans le SDK que j'utilise, ils sont toujours appelés enfants, enfantsRecursive, c'est pourquoi je viens d'utiliser cela.


7 Réponses :


3
votes

yep - voir cette section pour itérations récursives en utilisant C # Itérateurs.


0 commentaires

9
votes

Cela fera une première traversée de la technologie de profondeur de la boîte code> code> 'arbre'. Vous pouvez alors simplement appeler cette méthode sur la case code> code> pour renvoyer tous les enfants récursifs.

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


2 commentaires

Merci mais cela va-t-il passer par les enfants de chaque boîte.getchild (i) aussi?


Ah, maintenant ça a du sens. Édité la réponse.



1
votes
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;
      }
    }
  }
}
You can call this from BoxCollection, but since Box is already a collection of Boxes, I don't see what BoxCollection's purpose is here. For that matter, having Box implement IEnumerable<Box> or one of its descendants (ICollection<Box>, IList<Box>) would probably improve usefulness.It's also possible to do it in an iterative rather than recursive manner, which sometimes has a better performance (pretty much any time that the compiler doesn't turn the recursion into interation anyway), but recursive is more readable and normally more than performant enough.

0 commentaires

-1
votes

Bien sûr. Vous n'avez même pas vraiment besoin de BoxContainer, puisque la boîte, par son nom, existe sous la forme d'un conteneur: xxx

si la boîte a des boîtes de béquille B et C, BOX B TENUE BOXES DR E, et boîte C tenue Box F, l'énumérable sortirait A, B, D, E, C, F.


2 commentaires

Votre foreach entraîne effectivement un type de retour de ienumerable > qui ne correspond pas au type de retour déclaré. Je ne crois pas que cela compilait en C #. En F #, vous pouvez modifier le deuxième rendement dans un rendement ! et cela fonctionnerait bien.


Vous avez exactement raison. Éditer rapidement pour itérer à travers l'iEnumérable retourné par la boîte enfant.



1
votes

Oui, mais vous devez énumérer le résultat récursif. Vous ne pouvez pas simplement donner le retour, car le type ne correspond pas.

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


0 commentaires

13
votes

J'irais avec maintenir manuellement une pile au lieu de s'appuyer ici sur la pile d'appels ici. La raison en est qu'un nouveau iEnumerable code> devrait être créé pour chaque Spline code> visité si vous avez utilisé la pile d'appel en appelant récursivement la méthode qui obtient les descendants. Ce serait inefficace. Vous pouvez améliorer considérablement la traversée en utilisant votre propre pile.

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 commentaires

J'aimerais pouvoir voter cela 10 fois. C'est donc beaucoup plus efficace qu'une solution récursive et ne nécessite que quelques secondes de plus de réflexion. En fait, vous pouvez généraliser cela dans une fonction aplatissée pour simplifier tout itérateur récursif.


Cela renvoie également également l'élément racinaire, pas seulement les descendants réels.



0
votes

Cela ajoute à la grande réponse de Brian Gideon, qui fournit uniquement uniquement les descendants, sans l'élément racinaire. Il utilise en outre foreach code>, qui peut être disponible par exemple dans des contextes EF.

Voici mon code (): p>

/// <summary>
///     Retrieves all descendants.
/// </summary>
public IEnumerable<Item> Descendants {
    get {
        // This performs a simple iterative preorder traversal.
        Stack<Item> stack = new Stack<Item>(this.Children);
        while (stack.Count > 0) {
            Itemcurrent = stack.Pop();
            yield return current;

            //Push current's children
            foreach (Item currentChild in current.Children) {
                stack.Push(currentChild);
            }
        }
    }
}


0 commentaires