当不使用收益率(回报) [英] When NOT to use yield (return)

查看:213
本文介绍了当不使用收益率(回报)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有几个有用的问题,在这里因此,关于对收益率回报的好处。例如,

我在寻找的想法时不会使用收益率回报。例如,如果我希望需要返回所有项目的集合,它没有的似乎的如收益率将是有益的,对不对?

什么是其中将被限制,不必要的,给我带来麻烦,否则应避免使用收益率的情况下?

解决方案
  

什么是那里使用的产量将限制不必要的情况下,让我陷入麻烦,或以其他方式应避免?

这是一个好主意,仔细想想你使用的时候用递归定义的结构处理收益回报。例如,我经常看到这样的:

 公共静态的IEnumerable< T> preorderTraversal< T>(树< T>根)
{
    如果(根== NULL)产量突破;
    得到的回报root.Value;
    的foreach(在preorderTraversal牛逼项目(root.Left))
        产生收益的项目;
    的foreach(在preorderTraversal牛逼项目(root.Right))
        产生收益的项目;
}
 

非常明智的前瞻性code,但它的性能问题。假设树为h深。然后会有至多点为O(H)嵌套的迭代器内置。呼唤MoveNext的在外部迭代器则使O(高)嵌套调用对MoveNext。由于它这样做O(N)次,其中n项的树,这使得算法O(HN)。而且,由于二叉树的高度是LG N'LT; = H< = N,这意味着该算法充其量为O(n LG电子n)和最坏为O(n ^ 2)的时间,最好的情况下Ø (LG n)和最坏情况下为O(n)的堆栈空间。这是O(H)的堆空间,因为每个枚举被分配在堆上。 (在C#中实现我所知道的,符合标准的实现可能有其他的堆栈或堆空间的特性。)

但迭代树可以为O(n)在堆栈空间(1)时间和O操作。你可以这样写,如:

 公共静态的IEnumerable< T> preorderTraversal< T>(树< T>根)
{
    VAR堆栈=新的堆栈<树< T>>();
    stack.Push(根);
    而(stack.Count!= 0)
    {
        变种电流= stack.Pop();
        如果(当前== NULL)继续;
        得到的回报current.Value;
        stack.Push(current.Left);
        stack.Push(current.Right);
    }
}
 

它仍然使用产生回报,但更聪明了。现在,我们为O(n)的时间和O(h)上堆空间和O(1)中的堆栈空间。

延伸阅读:看到关于这个问题韦斯代尔的文章:

<一个href="http://blogs.msdn.com/b/wesdyer/archive/2007/03/23/all-about-iterators.aspx">http://blogs.msdn.com/b/wesdyer/archive/2007/03/23/all-about-iterators.aspx

There are several useful questions here on SO about the benefits of yield return. For example,

I'm looking for thoughts on when NOT to use yield return. For example, if I expect to need to return all items in a collection, it doesn't seem like yield would be useful, right?

What are the cases where use of yield will be limiting, unnecessary, get me into trouble, or otherwise should be avoided?

解决方案

What are the cases where use of yield will be limiting, unnecessary, get me into trouble, or otherwise should be avoided?

It's a good idea to think carefully about your use of "yield return" when dealing with recursively defined structures. For example, I often see this:

public static IEnumerable<T> PreorderTraversal<T>(Tree<T> root)
{
    if (root == null) yield break;
    yield return root.Value;
    foreach(T item in PreorderTraversal(root.Left))
        yield return item;
    foreach(T item in PreorderTraversal(root.Right))
        yield return item;
}

Perfectly sensible-looking code, but it has performance problems. Suppose the tree is h deep. Then there will at most points be O(h) nested iterators built. Calling "MoveNext" on the outer iterator will then make O(h) nested calls to MoveNext. Since it does this O(n) times for a tree with n items, that makes the algorithm O(hn). And since the height of a binary tree is lg n <= h <= n, that means that the algorithm is at best O(n lg n) and at worst O(n^2) in time, and best case O(lg n) and worse case O(n) in stack space. It is O(h) in heap space because each enumerator is allocated on the heap. (On implementations of C# I'm aware of; a conforming implementation might have other stack or heap space characteristics.)

But iterating a tree can be O(n) in time and O(1) in stack space. You can write this instead like:

public static IEnumerable<T> PreorderTraversal<T>(Tree<T> root)
{
    var stack = new Stack<Tree<T>>();
    stack.Push(root);
    while (stack.Count != 0)
    {
        var current = stack.Pop();
        if (current == null) continue;
        yield return current.Value;
        stack.Push(current.Left);
        stack.Push(current.Right);
    }
}

which still uses yield return, but is much smarter about it. Now we are O(n) in time and O(h) in heap space, and O(1) in stack space.

Further reading: see Wes Dyer's article on the subject:

http://blogs.msdn.com/b/wesdyer/archive/2007/03/23/all-about-iterators.aspx

这篇关于当不使用收益率(回报)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆