The key is keeping the state of the function between calls. You have a number of options:
Static (or global) state. Means the sequence of calls to the function is not reentrant, i.e. you can't have the function call itself recursively, nor can you have more than one caller running different sequences of calls.
Initialising (and possibly allocating) the state on or before the first call, and passing that to the function on each subsequent call.
Doing clever stuff with setjmp/longjmp, the stack, or modifiable code (there's an article somewhere about currying functions in C that creates an object with the necessary code to call the curried function; a similar technique could create an object with the function's state and the necessary code to save and restore it for each call). (Edit Found it -- http://asg.unige.ch/site/papers/Dami91a.pdf)
Greg cites an interesting article, above, that presents a way of using static state with syntax similar to the yield
statement. I liked it academically but probably wouldn't use it because of the reentrancy issue, and because I'm still surprised that the infamous Duffy's Device even compiles ;-).
In practice, large C programs do want to compute things lazily, e.g. a database server may want to satisfy a SELECT ... LIMIT 10
query by wrapping the plain SELECT
query inside something that will yield each row until 10 have been returned, rather than computing the whole result and then discarding most of them. The most C-like technique for this is explicitly create an object for the state, and to call a function with it for each call. For your example, you might see something like:
/* Definitions in a library somewhere. */
typedef int M2_STATE;
M2_STATE m2_new() { return 0; }
int m2_empty(M2_STATE s) { return s < INT_MAX; }
int m2_next(M2_STATE s) { int orig_s = s; s = s + 2; return orig_s; }
/* Caller. */
M2_STATE s;
s = m2_new();
while (!m2_empty(s))
{
int num = m2_next(s);
printf("%d\n", num);
}
This seems cumbersome for the multiples of two, but it becomes a useful pattern for more complicated generators. You can make the state more complicated without having to burden all the code that uses your generator with the details. Even better practice is to return an opaque pointer in the new
function, and (unless GC is available) provide a function for cleaning up the generator.
The big advantage of allocating the state for each new sequence of calls is things like recursive generators. For example, a generator that returns all files under a directory, by calling itself on each subdirectory.
char *walk_next(WALK_STATE *s)
{
if (s->subgenerator)
{
if (walk_is_empty(s->subgenerator))
{
walk_finish(s->subgenerator);
s->subgenerator = NULL;
}
else
return walk_next(s->subgenerator);
}
char *name = readdir(s->dir);
if (is_file(name))
return name;
else if (is_dir(name))
{
char subpath[MAX_PATH];
strcpy(subpath, s->path);
strcat(subpath, name);
s->subgenerator = walk_new(subpath);
return walk_next(s->subgenerator);
}
closedir(s->dir);
s->empty = 1;
return NULL;
}
(You'll have to excuse my misuse of readdir, et al. and my pretense that C has idiot-proof string support.)