屈服!运算符-实现和可能的C#等价物
我现在正在学习F#,我真的很喜欢屈服!运算符-实现和可能的C#等价物,c#,f#,ienumerable,yield,enumerators,C#,F#,Ienumerable,Yield,Enumerators,我现在正在学习F#,我真的很喜欢收益(收益率)运算符。不仅是因为它的名字,当然也因为它的功能 屈服操作符基本上允许您从序列表达式中生成序列的所有元素。这对于组合枚举数很有用。由于我经常遇到大型、复杂的枚举数,我对我们可以使用的策略感兴趣,这些策略可以将它们分解,并将它们与简单的枚举数组合起来 不幸的是,屈服了运算符在C#中不可用。据我所知,它就像一个foreach(源代码中的var x)产生x
收益代码>(收益率)运算符。不仅是因为它的名字,当然也因为它的功能
屈服
操作符基本上允许您从序列表达式中生成序列的所有元素。这对于组合枚举数很有用。由于我经常遇到大型、复杂的枚举数,我对我们可以使用的策略感兴趣,这些策略可以将它们分解,并将它们与简单的枚举数组合起来
不幸的是,屈服了代码>运算符在C#中不可用。据我所知,它就像一个foreach(源代码中的var x)产生x但是我正在读的书()表明它有更好的性能
- 那么F#编译器在这里到底做什么呢?(是的,我也可以用反射器来观察,但我想更详细地描述一下这个机制)
为了在C#中实现类似的构造,我探索了多种方法,但没有一种方法像yield代码>运算符,我也不确定它们的复杂性。如果我的BigO号码是正确的,有人可以提供输入吗
- 将枚举数分解为多个私有枚举数,然后从公共枚举数生成每个元素:
foreach (var x in part1()) yield x
foreach (var x in part2()) yield x
这将有效地导致每个元素的“双倍收益”。那是O(2n)吗?(或者可能更糟?)无论如何,使用这种方法可以阻止我使用收益率中断代码>来自我的任何子部分
- 将枚举数分解为多个私有枚举数,然后从公共枚举数中提取所有私有枚举数:
return part1().Concat(part2())
我相信这与前面提到的解决方案没有什么不同,因为Concat()
是以我上面概述的方式实现的
还有其他选择吗?在当前版本的C#中,我认为除了每个。。。收益率返回
和Concat
。我同意拥有收益率会很好运算符,它将使某些构造更加优雅,但我怀疑这个特性是否会被列入“必备”列表,因为我们可以轻松地不使用它
您可能对此感兴趣,它引入了一个新的yield-foreach
构造:
IEnumerable<XmlNode> Traverse(XmlNode n)
{
yield return n;
foreach (XmlNode c in n.ChildNodes)
yield foreach Traverse(c);
}
IEnumerable遍历(XmlNode)
{
收益率n;
foreach(n.ChildNodes中的XmlNode c)
每条导线的屈服(c);
}
关于你关于复杂性的问题:在这两种情况下都是O(n)。不使用O(2n),因为它表示与O(n)(线性)相同的复杂性。我认为用目前的C#特性,你再也做不到比这更好的了…没有直接对应的屈服代码>在C#中。您目前正面临着foreach
和yield return
的组合
然而,IIRC,LINQ提供了类似的东西,即SelectMany
query操作符,它将C转换为multiplefrom。。在..
子句中
(我希望我没有混淆两个不同的概念,但是IIRC,yield!
和SelectMany
本质上是“展平”投影;即对象的层次结构被“展平”为列表。)关于编译器如何翻译yield操作说明了第4.3节中的一种实现技术(特别是,他们跨越图7-9的示例说明了一般策略)。我不认为在C#中的迭代器块中有任何好的方法可以做到这一点-据我所知,您提出的解决方案,当递归使用时,它们都可能导致二次行为。您总是可以手动创建一个NestedEnumerable
子类来获得性能优势,但与使用普通迭代器块相比,这将非常难看。正如您引用的文章所指出的,当递归使用时,会有屈服的实例
(相当于C#中假设的收益率foreach
)为O(n),但foreach。。。收益率返回…
是O(n^2)。@kvb:对,这就是我说的,如果不清楚,很抱歉。