是否可以编写递归的IEnumerable <t> </t>

时间:2010-09-02 21:08:00

标签: c# .net collections recursion ienumerable

我有一个类:

class Spline
    int ChildrenCount;
    Spline GetChild (int index)

class SplineCollection : IEnumerable<Spline>
    Spline Master

是否有可能为SplineCollection写一个递归的IEnumerable,它会逐个返回所有的孩子?

编辑:所以Master是根Box,其子级的层次结构可以是任何深度。

编辑:通过使用名称Box,我认为我困惑了一些人。它意味着是一个几何对象,而不是一个容器。因此将其更改为Spline。

7 个答案:

答案 0 :(得分:10)

我会手动维护堆栈,而不是依赖于调用堆栈。原因是,如果您通过递归调用获取后代的方法使用调用堆栈,则必须为访问的每个IEnumerable<Spline>创建新的Spline。那将是低效的。您可以使用自己的堆栈来显着改善遍历。

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

答案 1 :(得分:9)

这将对Box'树'进行深度优先遍历。然后,您可以在Master框中调用此方法以返回所有递归子项。

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 :(得分:3)

是的 - 请参阅本节了解Recursive Iterations使用C#迭代器。

答案 3 :(得分:1)

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

你可以从BoxCollection中调用它,但由于Box已经是Box的集合,我不知道BoxCollection的用途是什么。就此而言,拥有Box实现IEnumerable<Box>或其后代之一(ICollection<Box>IList<Box>)可能会提高实用性。

也可以以迭代而不是递归的方式进行,有时候性能更好(几乎任何时候编译器都不会将递归转换为交互),但递归更具可读性,通常更多不够高效。

答案 4 :(得分:1)

是的,但您必须枚举递归结果。你不能只是回报它,因为类型不匹配。

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

答案 5 :(得分:0)

这增加了Brian Gideon的很好的回答,实际上只提供了后代,没有根元素。此外,它使用foreach,例如在EF上下文中可用。

这是我的代码():

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

答案 6 :(得分:-1)

不确定。你甚至不需要BoxContainer,因为box的名称存在于容器中:

public class Box
{
    private List<Box> myBoxes;

    public IEnumerable<Box> GetAllBoxes()
    {
        yield return this;
        foreach (var box in myBoxes)
        {
            var enumerator = box.GetAllBoxes().GetEnumerator();
            while(enumerator.MoveNext()) 
                yield return enumerator.Current;
        }
    }
}

如果方框A持有方框B和方框C,方框B持有方框D和方框E,方框C方向方框方向为方框F,则可枚举出A,B,D,E,C,F。