我有一个树状的结构。此结构中的每个元素都应该能够返回它所属的所有元素的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
)而不是?
答案 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;
最后。 : - )