On Mon, 11 Oct 2004 09:46:09 +0000 (UTC), A.Sloman@cs.bham.ac.uk
wrote:
>It is possible (though tricky sometimes) to implement your own tail
>recursion optimisation for particular procedures using 'chain'
>(see HELP CHAIN).
>
>E.g. a recursive listlength procedure (length is already in the system):
>
>define len(list);
> if null(list) then 0
> else
> ;;; put 1 and the tail on the stack, then chain to a procedure
> ;;; that runs len, then runs +
> 1, tl(list), chain(procedure(); len(); + endprocedure)
> endif
>enddefine;
>
>chain returns from its caller, then runs its argument.
>
>len([]) =>
>** 0
>
>len([a b c]) =>
>** 3
>
>It's ugly but it stops the stack from growing. Note that
Your example doesn't look correct: without checking, it looks to
me like the stack would still grow, here, with multiple copies
of the (anonymous) procedure
procedure(); len(); + endprocedure
It's all those pending calls of "+" that do it. Alternatively, it's
because of all those "1"s you are leaving on the user stack.
To correctly eliminate tail recursion here, one needs to remember
that it is *tail* recursion, i.e. the very last thing the
recursive function does is to call itself, not to call itself
then call "+".
However, I can only see two obvious ways to define len
recursively, with either:
else
1 + len(tl(list))
endif
or
else
len(tl(list)) + 1
endif
In both of these the "+" must be performed *after* the
recursion, which makes tail recursion impossible. (Your
solution corresponds to the first.)
To do tail recursion, we must perform the addition *before*
the recursive call, and this requires defining an auxiliary
procedure, (which can be done in a number of ways) e.g.:
define len(list);
len0(0, list)
enddefine;
define len0(count, list);
if null(list) then
count
else
chain(count+1, tl(list), len0);
endif
enddefine;
Maybe I ought to test this to see if I got it right ...
(Took me two attempts! Darn! But the above is now
correct.)
Note: the crucial point is that in a tail recursion
optimisation you are chaining to the very same procedure
that you are leaving, otherwise you aren't replacing
the stack frame by an identical stack frame.
Jonathan
--
Use jlc1 at address, not spam.
|