Does this have any equivalent in c?
No. On Windows, you can use fibers to achieve a similar effect though.
Fibers? Oh, this:
Yield Return Iterator for Native C++ Using Fibers
http://www.codeproject.com/KB/library/fiber-based_iterator.aspx
While C doesn't have the same concept as yield in regards to enumerating over a collection it does have the ability to create coroutines and fibers.
Here are some wikipedia articles which might be of interest:
http://en.wikipedia.org/wiki/Setcontext http://en.wikipedia.org/wiki/Setjmp/longjmp
No. However, you can achieve similar effects in C with the use of setjmp, longjmp, but it's very tricky.
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.
Coroutines in C uses some preprocessor hackery, but implements a fairly natural (relative to anything else in C) yield
.
Whereas you'd write this in 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
lets you write this in 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
For something more complex and requiring reentrancy, the Hamming numbers in 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
and 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
In C# yields simplifies the creation of IEnumberables for a collection.
In C++ you have to use STL Iterators.