[I think this was Army1987 as well, although the attribution was
shaved off before I got here:]
Charlton Wilbur said:
I strongly doubt these percentages. [snip]
Choose 1000 random C function which either call themselves (directly or
through another function) or contain a loop. ...
Most of my recursive functions tend to operate on trees. For
instance, here are two I have lying around. (This is an old version
of some old code, dating back to the early 1990s and hence pre-ANSI-C.
I did a quick edit job just now to convert it, and may have damaged
it a bit in the process, although I hope not.)
"struct nvlist" looks like this:
/*
* Name/value lists. Values can be strings or pointers and/or can carry
* integers. The names can be NULL, resulting in simple value lists.
*/
struct nvlist {
struct nvlist *nv_next;
const char *nv_name;
union {
const char *un_str;
void *un_ptr;
} nv_un;
#define nv_str nv_un.un_str
#define nv_ptr nv_un.un_ptr
int nv_int;
};
/*
* Eval an expression tree. Calls the given function on each node,
* passing it the given context & the name; return value is &/|/! of
* results of evaluating atoms.
*
* No short circuiting ever occurs. fn must return 0 or 1 (otherwise
* our mixing of C's bitwise & boolean here may give surprises).
*/
static int
expr_eval(struct nvlist *expr, int (*fn)(const char *, void *), void *context)
{
int lhs, rhs;
switch (expr->nv_int) {
case FX_ATOM:
return ((*fn)(expr->nv_name, context));
case FX_NOT:
return (!expr_eval(expr->nv_next, fn, context));
case FX_AND:
lhs = expr_eval(expr->nv_ptr, fn, context);
rhs = expr_eval(expr->nv_next, fn, context);
return (lhs & rhs);
case FX_OR:
lhs = expr_eval(expr->nv_ptr, fn, context);
rhs = expr_eval(expr->nv_next, fn, context);
return (lhs | rhs);
}
panic("expr_eval %d", expr->nv_int);
/* NOTREACHED */
}
/*
* Free an expression tree.
*/
static void
expr_free(struct nvlist *expr)
{
struct nvlist *rhs;
/* This loop traverses down the RHS of each subexpression. */
for (; expr != NULL; expr = rhs) {
switch (expr->nv_int) {
/* Atoms and !-exprs have no left hand side. */
case FX_ATOM:
case FX_NOT:
break;
/* For AND and OR nodes, free the LHS. */
case FX_AND:
case FX_OR:
expr_free(expr->nv_ptr);
break;
default:
panic("expr_free %d", expr->nv_int);
}
rhs = expr->nv_next;
nvfree(expr);
}
}
In general, while it is possible to flatten out recursion, it is
often not a great idea -- expr_eval() would be considerably more
complicated if written iteratively. But there are special cases,
and curiously, expr_free() is one of them: it uses a mix of recursion
and iteration, with recursion handling left-hand sides, and iteration
handling right-hand sides, of binary operators (AND and OR).