Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Yield in C#

Tags:

c++

c

c#

.net

Does this have any equivalent in c?

like image 217
Developer Avatar asked Nov 28 '22 02:11

Developer


2 Answers

Yield is implemented by the compiler as a custom class that implements a state machine. While you can't get the syntax as easily (unless you use the fiber method previously specified) you can replicate the results yourself pretty simply, although it's quite tedious. Here's how (I will show in C#, you'll have to do the appropriate thing in C++ depending on the types you are using):

Assuming the following code:

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) Unroll all foreach methods into explicit enumerator calls:

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) Move all local variables to the top of the method:

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) Move to a looping construct with a nested switch statement.
a) Change state and continue the loop for every yield return. b) Invert if conditions to c) Yield break for every exit condition (below we invert the 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) Move into a class and return the class from your method: a) yield breaks become "return false;" b) yield returns become "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) Optimize as appropriate.

like image 193
Talljoe Avatar answered Nov 29 '22 17:11

Talljoe


Fibers? Oh, this:

Yield Return Iterator for Native C++ Using Fibers
http://www.codeproject.com/KB/library/fiber-based_iterator.aspx

like image 45
Robert Harvey Avatar answered Nov 29 '22 17:11

Robert Harvey