繁体   English   中英

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

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

我正在学习F#,我真的很喜欢yield! (yield-bang)运营商。 不仅因为它的名字,而且它当然也是它的作用。

yield! operator基本上允许您从序列表达式中生成序列的所有元素。 这对于编写枚举器很有用。 由于我经常遇到大而复杂的调查员,我对策略很感兴趣,我们可以用它来分解它们,并从简单的枚举器中编写它们。

不幸的是, yield! 运算符在C#中不可用。 据我所知,它的作用就像foreach (var x in source) yield x; 但是我正在阅读的这本书( Petricek的真实世界F# - 曼宁 )表明它有更好的表现......

  • 那么F#编译器到底在做什么呢? (是的,我也可以使用Reflector来查看它,但我希望对机制有更详细的描述)。

为了在C#中实现类似的构造,我已经探索了多种方法,但它们都没有像yield!一样简洁yield! 操作员,我也不确定它们的复杂性。 如果我的BigO号码正确,有人可以提供输入吗?

  • 将枚举器分解为多个私有枚举器,然后从公共枚举器中生成每个元素:

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

    这将有效地导致每个元素的“双倍收益”。 那是O(2n)吗? (或者可能更糟?)无论如何,使用这种方法阻止我使用yield break; 来自我的任何子部分。

  • 将枚举器分解为多个私有枚举器,然后从公共枚举器中连接所有私有枚举器:

     return part1().Concat(part2()) 

    我相信这与上述解决方案没有什么不同,因为Concat()是按照我上面概述的方式实现的。

还有其他选择吗?

在当前版本的C#中,我认为除了foreach... yield return之外你还有其他选择foreach... yield returnConcat 我同意yield!会很高兴yield! C#中的运算符,它会使某些结构更加优雅,但我怀疑这个功能是否会成为“必备”列表,因为我们可以很容易地将其删除。

您可能对此MS研究论文感兴趣,该论文介绍了一种新的yield foreach构造:

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

关于复杂性的问题:在两种情况下都是O(n) 不使用O(2n) ,因为它表示与O(n) (线性)相同的复杂度。 我认为你不能用当前的C#功能做得更好......

关于编译器如何转换yield! 操作,Thomas Levesque在他的回答中引用的论文说明了4.3节中的一种实现技术(特别是,他们的示例跨越图7-9说明了一般策略)。 我不认为在C#中的迭代器块中有任何好方法可以做到这一点 - 因为我理解你提出的解决方案,它们在递归使用时都会导致二次行为。 您总是可以手动创建NestedEnumerable<T>子类来实现性能优势,但与使用普通迭代器块相比,这将非常难看。

yield!没有直接的对应物yield! 在C#中。 你现在仍然坚持使用foreachyield return

但是,IIRC,LINQ提供类似的东西,即SelectMany查询运算符,它将C#转换为from .. in ..子句中的多个。

(我希望我不会混淆两个不同的概念,但是IIRC,两者都yield!SelectMany基本上是“扁平化”投影;即,对象的层次结构被“扁平化”为一个列表。)

暂无
暂无

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

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