C#的收益率

时间:2009-06-10 21:42:59

标签: c# .net c++ c

这在c中是否有任何等价物?

7 个答案:

答案 0 :(得分:11)

Yield由编译器实现为实现状态机的自定义类。虽然您无法轻松获得语法(除非您使用之前指定的光纤方法),您可以非常简单地复制结果,尽管它非常繁琐。这是如何(我将在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)进入一个类并从您的方法返回该类:    a)收益率突破变得“回归虚假”;    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)适当优化。

答案 1 :(得分:8)

纤维?哦,这个:

使用光纤的Native C ++的Yield Return Iterator
http://www.codeproject.com/KB/library/fiber-based_iterator.aspx

答案 2 :(得分:7)

虽然C在枚举集合方面没有与yield相同的概念,但它确实能够创建协同程序和光纤。

以下是一些可能感兴趣的维基百科文章:

http://en.wikipedia.org/wiki/Setcontext http://en.wikipedia.org/wiki/Setjmp/longjmp

答案 3 :(得分: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 

对于更复杂且需要重入的东西,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 

答案 4 :(得分:2)

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

答案 5 :(得分:1)

没有。但是,您可以使用setjmp, longjmp在C中实现类似的效果,但非常非常棘手。

答案 6 :(得分:1)

在C#中,简化了集合的IEnumberables的创建。

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