[英]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# - 曼宁 )表明它有更好的表现......
为了在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 return
和Concat
。 我同意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#中。 你现在仍然坚持使用foreach
和yield return
。
但是,IIRC,LINQ提供类似的东西,即SelectMany
查询运算符,它将C#转换为from .. in ..
子句中的多个。
(我希望我不会混淆两个不同的概念,但是IIRC,两者都yield!
而SelectMany
基本上是“扁平化”投影;即,对象的层次结构被“扁平化”为一个列表。)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.