2009-06-10 103 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迭代器。