简体   繁体   English

F#收益! operator - 实现和可能的C#等价物

[英]F# yield! operator - Implementation and possible C# equivalents

I'm currently learning F# and I really love the yield! 我正在学习F#,我真的很喜欢yield! (yield-bang) operator. (yield-bang)运营商。 Not only for its name but also for what it does of course. 不仅因为它的名字,而且它当然也是它的作用。

The yield! yield! operator basically allows you to yield all elements of a sequence from a sequence expression. operator基本上允许您从序列表达式中生成序列的所有元素。 This is useful for composing enumerators. 这对于编写枚举器很有用。 Since I regularly encounter big, complicated enumerators I am interested in strategies we can use to break them up and compose them from simpler enumerators. 由于我经常遇到大而复杂的调查员,我对策略很感兴趣,我们可以用它来分解它们,并从简单的枚举器中编写它们。

Unfortunatetely, the yield! 不幸的是, yield! operator is not available in C#. 运算符在C#中不可用。 As far as I understand, what it does is like a foreach (var x in source) yield x; 据我所知,它的作用就像foreach (var x in source) yield x; but the book I'm reading ( Petricek's Real World F# - Manning ) suggests that it has better performance... 但是我正在阅读的这本书( Petricek的真实世界F# - 曼宁 )表明它有更好的表现......

  • So what exactly does the F# compiler do here? 那么F#编译器到底在做什么呢? (Yes, I can look at it using Reflector too but I'd like to have a more detailed description of the mechanism). (是的,我也可以使用Reflector来查看它,但我希望对机制有更详细的描述)。

In order to achieve a similar construct in C# I have explored multiple ways, but none of them is as concise as the yield! 为了在C#中实现类似的构造,我已经探索了多种方法,但它们都没有像yield!一样简洁yield! operator and I'm also not sure about the complexity of them. 操作员,我也不确定它们的复杂性。 Could someone please provide input if my BigO numbers are correct? 如果我的BigO号码正确,有人可以提供输入吗?

  • Decompose enumerator into multiple private enumerators and then yield each element from the public enumerator: 将枚举器分解为多个私有枚举器,然后从公共枚举器中生成每个元素:

     foreach (var x in part1()) yield x foreach (var x in part2()) yield x 

    This will effectively result in a "double yield" on each element. 这将有效地导致每个元素的“双倍收益”。 Is that O(2n) then? 那是O(2n)吗? (or possibly worse?) Anyway, using this approach stops me from using yield break; (或者可能更糟?)无论如何,使用这种方法阻止我使用yield break; from any of my subparts. 来自我的任何子部分。

  • Decompose enumerator into multiple private enumerators and then concat all private enumerators from the public enumerator: 将枚举器分解为多个私有枚举器,然后从公共枚举器中连接所有私有枚举器:

     return part1().Concat(part2()) 

    I believe this is no different from the aforementioned solution because Concat() is implemented the way I outlined above. 我相信这与上述解决方案没有什么不同,因为Concat()是按照我上面概述的方式实现的。

Any other options? 还有其他选择吗?

In the current version of C#, I don't think you have other options than foreach... yield return and Concat . 在当前版本的C#中,我认为除了foreach... yield return之外你还有其他选择foreach... yield returnConcat I agree it would be nice to have the yield! 我同意yield!会很高兴yield! operator in C#, it would make certain constructs much more elegant, but I doubt this feature will ever make it to the "must-have" list, since we can easily do without it. C#中的运算符,它会使某些结构更加优雅,但我怀疑这个功能是否会成为“必备”列表,因为我们可以很容易地将其删除。

You might be interested in this MS research paper , which introduces a new yield foreach construct : 您可能对此MS研究论文感兴趣,该论文介绍了一种新的yield foreach构造:

IEnumerable<XmlNode> Traverse(XmlNode n)
{
    yield return n;
    foreach (XmlNode c in n.ChildNodes)
        yield foreach Traverse(c);
}

Regarding your question about complexity: in both cases it's O(n) . 关于复杂性的问题:在两种情况下都是O(n) O(2n) is not used, because it denotes the same complexity as O(n) (linear). 不使用O(2n) ,因为它表示与O(n) (线性)相同的复杂度。 I don't think you can do better than that with the current C# features... 我认为你不能用当前的C#功能做得更好......

Regarding how the compiler translates the yield! 关于编译器如何转换yield! operation, the paper cited by Thomas Levesque in his answer illustrates one implementation technique in section 4.3 (in particular, their example spanning figures 7-9 is illustrative of the general strategy). 操作,Thomas Levesque在他的回答中引用的论文说明了4.3节中的一种实现技术(特别是,他们的示例跨越图7-9说明了一般策略)。 I don't think that there's any good way to do this from within an iterator block in C# - as I understand your proposed solutions, they could both result in quadratic behavior when used recursively. 我不认为在C#中的迭代器块中有任何好方法可以做到这一点 - 因为我理解你提出的解决方案,它们在递归使用时都会导致二次行为。 You could always manually create a NestedEnumerable<T> subclass to achieve the performance benefits, but this will be quite ugly compared to using a normal iterator block. 您总是可以手动创建NestedEnumerable<T>子类来实现性能优势,但与使用普通迭代器块相比,这将非常难看。

There is no direct counterpart to yield! yield!没有直接的对应物yield! in C#. 在C#中。 You're currently stuck with a combination of foreach and yield return . 你现在仍然坚持使用foreachyield return

However, IIRC, LINQ offers something similar, namely the SelectMany query operator, which translates to C# as multiple from .. in .. clauses. 但是,IIRC,LINQ提供类似的东西,即SelectMany查询运算符,它将C#转换为from .. in ..子句中的多个。

(I'm hoping I'm not mixing up two different concepts, but IIRC, both yield! and SelectMany are essentially "flattening" projections; ie. a hierarchy of objects is "flattened" into a list.) (我希望我不会混淆两个不同的概念,但是IIRC,两者都yield!SelectMany基本上是“扁平化”投影;即,对象的层次结构被“扁平化”为一个列表。)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM