树中嵌套产量的性能

时间:2009-06-25 10:07:15

标签: c# performance ienumerable yield

我有一个树状的结构。此结构中的每个元素都应该能够返回它所属的所有元素的Enumerable。我们称这个方法为IEnumerable<Foo> GetAll()。所以,如果我们有

      A <-- topmost root
    /   \
   B     C
  / \   / \
  D  E  F  G

对元素GetAll的{​​{1}}调用会返回C(元素的固定顺序会很好,但不需要)。我想每个人都已经知道了。

{C, F, G}的当前实现如下所示:

GetAll

在早期的实现中,我返回了一个List,并使用public IEnumerable<Foo> GetAll () { yield return this; foreach (Foo foo in MyChildren) { foreach (Foo f in foo.GetAll ()) { yield return f; } } } 添加了子目标。

我的问题是,是否正确实施了使用产量的版本,或者是否应该改进(特别是在性能方面)。或者这只是坏事,我应该坚持List.AddRange() s(或List)而不是?

5 个答案:

答案 0 :(得分:20)

如果您将recurse展开到堆栈,则可以提高性能,因此您将只有一个迭代器:

public IEnumerable<Foo> GetAll()
{
    Stack<Foo> FooStack = new Stack<Foo>();
    FooStack.Push(this);

    while (FooStack.Count > 0)
    {
        Foo Result = FooStack.Pop();
        yield return Result;
        foreach (Foo NextFoo in Result.MyChildren)
            FooStack.Push(NextFoo);
    }
}

答案 1 :(得分:10)

在性能方面肯定不是理想的 - 你最终会为大树创建大量的迭代器,而不是一个知道如何有效遍历的迭代器。

关于此的一些博客文章:

值得注意的是,F#与“yield foreach”相当于“yield!

答案 2 :(得分:2)

更好的解决方案可能是创建一个递归遍历树的访问方法,并使用它来收集项目。

这样的事情(假设是二叉树):

public class Node<T>
{
    public void Visit(Action<T> action)
    {
        action(this);
        left.Visit(action);
        right.Visit(action);
    }

    public IEnumerable<Foo> GetAll ()
    {
        var result = new List<T>();
        Visit( n => result.Add(n));
        return result;
    }
}

采用这种方法

  • 避免创建大量嵌套迭代器
  • 避免创建超出必要的列表
  • 效率相对
  • 如果您只需要定期列出部分内容,则会失败

答案 3 :(得分:1)

不,那看起来不错。

看一下我的blog entry,它可能有用:)

答案 4 :(得分:-1)

根据我以前的经验,使用yield比创建List更有效。 如果您使用的是.NET 3.5,那么此实现应该没问题。但不要忘记

yield break;

最后。 : - )