在 C# 中生成
Posted
技术标签:
【中文标题】在 C# 中生成【英文标题】:Yield in C# 【发布时间】:2009-06-10 21:42:58 【问题描述】:这在 c 中有什么等价物吗?
【问题讨论】:
为什么需要yield?如果你有一个数组,你可以遍历它。 Yield 与 IEnumerables 相关,您通常会在“foreach”情况下使用它。既然你不能'foreach',为什么你需要创建一个'yield'? 【参考方案1】:Yield 由编译器实现为实现状态机的自定义类。虽然您无法轻松获得语法(除非您使用之前指定的 Fiber 方法),但您可以非常简单地自己复制结果,尽管这很乏味。下面是方法(我将在 C# 中展示,您必须根据您使用的类型在 C++ 中做适当的事情):
假设如下代码:
public IEnumerable<T> GetOddStrings(
IEnumerable<IEnumerable<string>> stringCollections)
foreach(var stringCollection in stringCollections)
foreach(var str in stringCollection)
if(str.Length %2 != 0) yield return str;
if(str.Length == 42) yield break; // 42 is BAD! Stop immediately
1) 将所有 foreach 方法展开为显式枚举器调用:
public IEnumerable<T> GetOddStrings(
IEnumerable<IEnumerable<string>> stringCollections)
var firstEnumerator = stringCollection.GetEnumerator();
while(firstEnumerator.MoveNext())
var secondEnumerator = firstEnumerator.Current.GetEnumerator();
while(secondEnumerator.MoveNext())
var str= secondEnumerator.Current;
if(str.Length %2 != 0) yield return str;
if(str.Length == 42) yield break;
2) 将所有局部变量移动到方法的顶部:
public IEnumerable<T> GetOddStrings(
IEnumerable<IEnumerable<string>> stringCollections)
IEnumerator<IEnumerable<string>> firstEnumerator;
IEnumerator<string> secondEnumerator;
string str;
firstEnumerator = stringCollections.GetEnumerator();
while(firstEnumerator.MoveNext())
secondEnumerator = firstEnumerator.Current.GetEnumerator();
while(secondEnumerator.MoveNext())
str= secondEnumerator.Current;
if(str.Length %2 != 0) yield return str;
if(str.Length == 42) yield break;
3) 移动到带有嵌套 switch 语句的循环结构。 a) 更改状态并为每个收益返回继续循环。 b) 反转 if 条件 c) 每个退出条件的屈服中断(下面我们反转 if)。
public IEnumerable<T> GetOddStrings(
IEnumerable<IEnumerable<string>> stringCollections)
IEnumerator<IEnumerable<string>> firstEnumerator;
IEnumerator<string> secondEnumerator;
string str;
int state = 0;
while(true)
switch(state)
case 0:
firstEnumerator = stringCollections.GetEnumerator();
// This could be "yield break" but I want to show how you
// could split ifs with significant code in the else
if(!firstEnumerator.MoveNext())
state = 1;
continue;
secondEnumerator = firstEnumerator.Current;
if(!secondEnumerator.MoveNext) continue;
state = 2;
if(str.Length %2 != 0) yield return str;
continue;
case 1:
yield break;
case 2:
if(str.Length == 42) yield break;
state = 0;
continue;
4) 进入一个类并从您的方法中返回该类: a)yield break 变成“return false”; b) 收益回报变成“this.Current = ??; return true;”
public IEnumerable<T> GetOddStrings(
IEnumerable<IEnumerable<string>> stringCollections)
return new OddStringEnumerable(stringCollections);
private class OddStringEnumerable : IEnumerable<string>
IEnumerable<IEnumerable<string>> stringCollections;
IEnumerator<IEnumerable<string>> firstEnumerator;
IEnumerator<string> secondEnumerator;
string str;
int state;
public OddStringEnumerable(IEnumerable<IEnumerable<string>> stringCollections)
this.stringCollections = stringCollections;
public string Current get; private set;
public bool MoveNext()
while(true)
switch(state)
case 0:
firstEnumerator = this.stringCollections.GetEnumerator();
if(!this.firstEnumerator.MoveNext())
this.state = 1;
continue;
this.secondEnumerator = this.firstEnumerator.Current;
if(!secondEnumerator.MoveNext) continue;
this.state = 2;
if(str.Length %2 != 0)
this.Current = str;
return true;
continue;
case 1:
return false;
case 2:
if(str.Length == 42) return false;
this.state = 0;
continue;
5) 酌情优化。
【讨论】:
【参考方案2】:纤维?哦,这个:
使用 Fibers 的原生 C++ 的收益返回迭代器http://www.codeproject.com/KB/library/fiber-based_iterator.aspx
【讨论】:
【参考方案3】:虽然 C 在枚举集合方面没有与 yield 相同的概念,但它确实具有创建协程和纤维的能力。
这里有一些可能感兴趣的***文章:
http://en.wikipedia.org/wiki/Setcontext http://en.wikipedia.org/wiki/Setjmp/longjmp
【讨论】:
我认为这太复杂了。最好的方法是只创建一个包含 yieldindex 变量和数组的结构,然后每次“yield”时增加索引。这样你的'yield'只是从yieldindex点中提取数组值。 我并不是说它不复杂,只是那是 C 的等价物。有很多方法可以解决这个问题...这就是我们编写软件的原因 =)【参考方案4】:Coroutines in C 使用了一些预处理程序,但实现了一个相当自然的(相对于 C 中的任何其他内容)yield
。
而你会用 Python 写这个:
"""This is actually a built-in function.
def range(start, stop, step):
i = start
while i < stop:
yield i
i = i + step
"""
if __name__ == '__main__':
import sys
start = int(sys.argv[1]) if len(sys.argv) > 2 else 0
stop = int(sys.argv[2]) if len(sys.argv) > 2 else int(sys.argv[1])
step = int(sys.argv[3]) if len(sys.argv) > 3 else 1
for i in range(start, stop, step):
print i,
print
coroutine.h
让您可以用 C 语言编写:
#include <coroutine.h>
#include <stdio.h>
int range(int start, int stop, int step)
static int i;
scrBegin;
for (i = start; i < stop; i += step)
scrReturn(i);
scrFinish(start - 1);
int main(int argc, char **argv)
int start, stop, step, i;
start = argc > 2 ? atoi(argv[1]) : 0;
stop = argc > 2 ? atoi(argv[2]) : atoi(argv[1]);
step = argc > 3 ? atoi(argv[3]) : 1;
while ((i = range(start, stop, step)) >= start)
printf("%d ", i);
printf("\n");
$ cc 范围.c
$ ./a.out 10
0 1 2 3 4 5 6 7 8 9
对于更复杂且需要重入的东西,Python 中的Hamming numbers:
def hamming():
yield 1
i2 = (2*x for x in hamming())
i3 = (3*x for x in hamming())
i5 = (5*x for x in hamming())
m2, m3, m5 = i2.next(), i3.next(), i5.next()
while True:
if m2 < m3:
if m2 < m5:
yield m2
m2 = i2.next()
else:
if m2 > m5: yield m5
m5 = i5.next()
elif m2 == m3: m3 = i3.next()
elif m3 < m5:
yield m3
m3 = i3.next()
else:
if m3 > m5: yield m5
m5 = i5.next()
if __name__ == '__main__':
import sys
it = hamming()
for i in range(str(sys.argv[1]) if len(sys.argv) > 1 else 25):
print it.next(),
print
和C:
#include <coroutine.h>
#include <stdio.h>
int hamming(ccrContParam)
ccrBeginContext;
ccrContext z[3];
int m2, m3, m5;
ccrEndContext(state);
ccrBegin(state);
state->z[0] = state->z[1] = state->z[2] = 0;
ccrReturn(1);
#define m2_next (2*hamming(&state->z[0]))
#define m3_next (3*hamming(&state->z[1]))
#define m5_next (5*hamming(&state->z[2]))
state->m2 = m2_next, state->m3 = m3_next, state->m5 = m5_next;
while (1)
if (state->m2 < state->m3)
if (state->m2 < state->m5)
ccrReturn(state->m2);
state->m2 = m2_next;
else
if (state->m2 > state->m5) ccrReturn(state->m5);
state->m5 = m5_next;
else if (state->m2 == state->m3) state->m3 = m3_next;
else if (state->m3 < state->m5)
ccrReturn(state->m3);
state->m3 = m3_next;
else
if (state->m3 > state->m5) ccrReturn(state->m5);
state->m5 = m5_next;
ccrFinish(-1);
int main(int argc, char **argv)
int count = argc > 1 ? atoi(argv[1]) : 25, i;
ccrContext z = 0;
for (i = 0; i < count; i++)
printf("%d ", hamming(&z));
printf("\n");
$ cc hamming.c
$ ./a.out
1 2 3 4 5 6 8 9 10 12 15 16 18 20 24 25 27 30 32 36 40 45 48 50 54
【讨论】:
【参考方案5】:没有。不过,在 Windows 上,您可以使用纤维来实现类似的效果。
【讨论】:
【参考方案6】:没有。但是,您可以使用setjmp, longjmp 在 C 中实现类似的效果,但这非常很棘手。
【讨论】:
【参考方案7】:在 C# 中,yield 简化了为集合创建 IEnumberables。
在 C++ 中,您必须使用 STL 迭代器。
【讨论】:
以上是关于在 C# 中生成的主要内容,如果未能解决你的问题,请参考以下文章