2009-06-10 82 views
5

这是否与c有任何等价关系?C中的收益率#

+2

甚至没有关闭! – 2009-06-10 21:48:39

+0

为什么你需要产量?如果你有一个数组,你可以迭代它。收益与IEnumerables相关,您通常会在'foreach'情况下使用它。既然你不能'foreach'你为什么需要创建'yield'? – DevinB 2009-06-12 14:05:42

回答

2

否。在Windows上,您可以使用光纤实现类似的效果。

1

不可以。不过,就可以实现与使用setjmp, longjmp用C 类似的效果,但它是非常棘手。

11

产量由编译器实现为实现状态机的自定义类。虽然不能轻松获得语法(除非您使用之前指定的光纤方法),但您可以非常简单地复制结果,虽然它非常繁琐。下面是(我将在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)如果条件为 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)移动到一个类,并从你的方法返回类:一 )产量突破成为“返回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)根据需要进行优化。

5

Coroutines in C使用了一些预处理器hackery,但实现了相当自然的(相对于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 range.c 
$ ./a.out 10 
0 1 2 3 4 5 6 7 8 9 

对于更复杂的东西,并要求重入的Hamming numbers在Python :

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 
1

在C#收益简化的集合创建IEnumberables的。

在C++中,你必须使用STL迭代器。