[comp.lang.misc] Iteration without coroutines

chris@mimsy.UUCP (Chris Torek) (03/21/88)

In article <1139@PT.CS.CMU.EDU> edw@IUS1.CS.CMU.EDU (Eddie Wyatt) writes:
>   But do you need coroutines to implimented generlized iteration. ...

>	void iterate_inorder(root,func)
>	    TREE *root;
>	    void (*func)();
>	    {
>	    if (root != NULLPTR(TREE))
>		{
>	    	iterate_inorder(root->left,func);
>	    	func(root->val);
>	    	iterate_inorder(root->right,func);
>		}
>	    }
>
>    I do see one problem though, which is dealing with variable
>    referenced  inside the loop.  They would have to become
>    global.

This replaces coroutine iteration with stack recursion; I think the
two are provably equivalent (but I am not going to [try to] prove it
here).

The variables need not be global; and indeed, I added a similar routine
to Gosling Emacs in a situation in which I wanted the variables to be
local.  The code (real, working, code, in all its gory, I mean glory)
is as follows:

/*
 * Iterate over a table, calling the given function with each entry
 * and with the given pointer.  The table is first sorted.
 */
TableIterate(t, f, a)
	register struct table *t;
	register int (*f)();
	caddr_t a;		/* this is a `generic pointer' */
{
	register int n;

	if (t->t_sorted <= 0)
		SortEntries(t);
	for (n = 0; n < t->t_size; n++)
		(*f)(t->t_ents[n], a);
}

[and in another file]

/*
 * Apropos matching is carried out in a context consisting of a keyword,
 * the original buffer, and a flag that is set whenever a buffer specific
 * variable is matched.
 */
struct AproposContext {
	char	*keyword;
	struct	buffer *old;
	int	AnyBufferSpecific;
};

static
AproposVarMatch(te, context)
	struct tentry *te;
	caddr_t context;
{
	register struct AproposContext *c = (struct AproposContext *)context;
	struct Binding *b;
	int type;
	char buf[100];

	/* ignore it if the name matches nowhere */
	if (sindex(te->te_name, c->keyword) == 0)
		return;
	/* or if the variable is unbound */
	if ((b = tetod(te, struct VariableName *)->v_binding) == NULL)
		return;
	/* footnote any variables that are buffer specific */
	if (b->BufferSpecific || b->IsDefault ||
	    b->IsSystem && b->b.b_Default) {
		type = '*';
		c->AnyBufferSpecific = 1;
	} else
		type = ' ';
	/* print name and (truncated to `reasonable' length) value */
	(void) sprintfl(buf, sizeof buf, "%-30s%c = %s\n", te->te_name,
		type, CanonicalValue(b, c->old));
	InsStr(buf);
}

[AproposMacMatch deleted; it is much the same]

static
DoApropos(context)
	caddr_t context;
{
	register struct AproposContext *c = (struct AproposContext *)context;

	c->AnyBufferSpecific = 0;
	TableIterate(&MacTable, AproposMacMatch, c);
	TableIterate(&VarTable, AproposVarMatch, c);
	if (c->AnyBufferSpecific)
		InsCStr("\n*Buffer Specific\n", 18);
}

static
Apropos()
{
	struct AproposContext context;

	if ((context.keyword = getnbstr(": apropos keyword: ")) == NULL)
		return (0);
	context.old = bf_cur;
	ShowSomething("Help", (char *)NULL, DoApropos, (caddr_t)&context);
	return (0);
}

[Incidentally, ShowSomething itself uses another similar iterator.
Perhaps the fanciest of all is in the routine that gives help for
partial matches; when I rewrote it, I made it produce a column-sorted
list of possible matches, where the columns are of the width deemed
most appropriate given the length of the matching strings.  But that
one uses global variables.]
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris