Does this have any equivalent in c?
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.
Fibers? Oh, this:
Yield Return Iterator for Native C++ Using Fibers
http://www.codeproject.com/KB/library/fiber-based_iterator.aspx
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With