Path: utzoo!attcan!uunet!lll-winken!lll-tis!ames!mailrus!uflorida!novavax!proxftl!bill
From: bill@proxftl.UUCP (T. William Wells)
Newsgroups: comp.misc
Subject: Re: Basics of Program Design
Message-ID: <464@proxftl.UUCP>
Date: 13 Jul 88 06:59:06 GMT
References: <901@td2cad.intel.com> <3061@rpp386.UUCP> <395@proxftl.UUCP> <53374@ti-csl.CSNET>
Distribution: na
Organization: Proximity Technology, Ft. Lauderdale
Lines: 212

Sigh....  Some people just can't seem to read.

My first posting in this series, <395@proxftl.UUCP>:

1) Queried the definition of tail recursion.

2) Lambasted a poster for saying an algorithm was good because:
   "any good compiler [would get rid of the recursion]", in
   spite of the fact that essentially all C compilers do not.

3) Asserted that the choice of algorithms is determined by
   pragmatic factors, not just the theoretical characteristics
   of the algorithm.

In my next posting, <430@proxftl.UUCP>, I:

1) Said that I've been in this business a long time and have
   worked with a variety of systems.

2) Pointed out that the only compiler that anyone says can do
   tail recursion, gcc, applies to a single processor, and that
   my work requires porting to over a hundred compilers.

3) Asserted that because of this I can not rely on compilers to
   do tail recursion.

4) Pointed out that tail recursion is equivalent to a while loop
   and that is the preferred method of coding this kind of
   algorithm. [The implied context is C, I'd not say this for
   some other languages.]

5) Asserted that recursion often needs to be checked carefully
   because it consumes resources.

6) Gave examples of how I dealt with some recursion: one by
   leaving it, it being the better code, and another by fixing
   it, as its unbounded recursion would cause problems.

7) Responded to a gratuitous insult with one of my own.

8) Pointed out that I have a lot of experience in optimizing C
   programs.

9) Proposed that the argument that a routine is going to be used
   a small fraction of the time is no excuse for sloppy coding.

10) Gave an example of where that approach could cost one, and
   another example where a sloppy implementation of sscanf
   accounted for 35% of the execution time of a program.

11) Pointed out the way to become the best programmer you can be
   is to try to program as well as possible, all the time.

12) Pointed out that a recursive strlen is just plain stupid.

Nowhere did I suggest that there is anything wrong with
recursion per se.  So let me say this real clear for those of you
insist on interpolating your own fears and fanaticisms into
other's writings.

	Recursion is a programming tool.  Like any tool, it has
	its uses and limitations.  Under certain circumstances,
	coding it any way but recursive is absolute stupidity.
	Under others, using recursion should get you condemned
	to writing RPG programs for the rest of your life.  In
	many other circumstances, a strong case can be made for
	using recursion, or for doing it some other way.  And in
	other cases, the use of recursion is purely a matter of
	taste.

With that taken care of, I'll address the points raised in the
article I am responding to:

In article <53374@ti-csl.CSNET>, gateley@mips.csc.ti.com (John Gateley) writes:
> In article <430@proxftl.UUCP> bill@proxftl.UUCP (T. William Wells) writes:
> >[...]
> >our code is regularly compiled with. Relying on compilers to do
> >tail recursion optimization would be slitting our own throats.
> >[...]
> >Sorry, but using purely tail recursive routines does not save
> >time for this programmer. Anywhere you would use tail recursion,
> >I'd automatically code a while loop.  (See below for why.) This
> >[...]
> >Also, another real-world point: recursive routines in general
> >need to be used carefully, to avoid excess resource consumption.
> >As an example, I recently translated a rather complicated parsing
> >routine from SCHEME to C. In the translated version were several
> >recursive routines. One of the recursive routines was left alone,
> >the greatest depth of recursion was the length of a grammar rule
> >and removing the recursion would have been a real pain.  Another
> >got fixed (even though it required extensive recoding) because it
> >was not possible to bound the stack usage. Our customers WILL
> >NOT let us chew resources with wild abandon.
> >[...]
> >efficient coding becomes second nature. Then you will naturally
> >write efficient programs and as a matter of course will not do
> >stupid things like writing strlen() recursively.
>
> The tail-recursion debate has been going on for a while, and I would
> like to defend tail recursion, recursion, and optimizations in general
> against the points made above.

There is flatly no defense against those points.  Fortunately,
the rest of your article does not really try to defend against
them, so you actually say something useful.

1) It is a fact that our code is ported to compilers that do not
   handle recursion well. This is not avoidable.  (Do YOU want to
   write me a good compiler for an 8051 or a 7809?) We can not
   avoid this fact; therefore, we must deal with it. That means
   that often we may not write routines that do extensive
   recursion. And for others, should you want to write portable
   code, you ought not to assume that you have megabytes of
   stack space. You won't.

2) Tail recursion, as a programming technique, is equivalent to
   iteration. Given that most compilers do not properly handle
   tail recursion, that makes the iteration the better coding
   technique. Unless, of course, one is a gcc chauvinist, in
   which case, one's opinions are irrelevant.

3) Like any other programming technique, recursion has its
   costs; failure to properly consider them, in the context of
   the task at hand, is bad programming. Period.

4) Strlen has absolutely no business being coded recursively.
   And while a recursive strlen algorithm might be an
   interesting exercise in alternative thinking, it is simply
   the wrong way to do it. It is just as silly as those example
   factorial algorithms which I suspect mislead the individual
   who wrote the strlen. An algorithm implements an idea; the
   idea of strlen is that of the number of characters in a
   string.  The recursive implementation of it simply fails to
   capture this property, except, perhaps, to a mathematician, or
   someone else who counts using Peano's axioms.

>                                First: tail recursion has been only
> discussed as a "true" recursive call optimization in these postings.
> It also applies to many non-recursive and indirectly recursive calls.
> Tail recursion optimizations serve several more purposes than what has
> been brought out here: it does not require pushing a return address on
> the stack (no stack growth, this is the optimization that has been discussed
> here); it allows more efficient argument passing (in the self-recursive
> case, all you have to do is change the arguments on the stack, not push
> new ones); it eliminates the need for saving register variables on the
> stack; and probably several more that I dont know about. I prefer these
> optimizations to take place.

These are good points in favor of adding these optimizations to a
compiler; they have to be balanced against the fact that there
is an almost unbreakable vicious circle that will make it
unlikely that these optimizations will become general.

>                              It has been mentioned that it is hard to
> debug with a tail-recursion optimizing compiler. I disagree and I use
> one every day.

I too thought that objection was silly.

> As for recursion: it is a tool just like while loops. It is a useful
> abstraction: many algorithms are much more simple in a recursive than
> non recursive form (my opinion of course). I feel that in order to get
> the most out of a language, it should have good tools (including recursion).
> It is up to the compiler writers to make those tool perform well (including
> being efficient). For example, there is a simple transformation which
> completely removes the stack from a recursive program (translating the
> program into continuation passing style). This is useful for compiler
> writers: they can use it where appropriate to make the program execute
> more efficiently.

I agree with this; however, remember that we are writing
programms today, we can't write inefficient programs, giving the
excuse that someday a compiler writer will make them efficient
for us. (Well, ok, we can; would you hire someone who thought
like that?) [B.T.W., doesn't a continuation have a context, and
isn't that context equivalent to an activation record?]

> I have a story which is similar to some that have been posted. In college,
> one of my homework assigments was to determine if a graph was connected.
> The graph was represented as a array with 1's for edges. I thought that
> recursion was terribly inefficient, so I just multiplied the matrix against
> itself the neccesary number of times. I also did it recursively. Guess which
> one was faster! The recursive one of course.

A good illustration of the tool-ness of techniques.  Another
example of this is a parsing algorithm that is O(n**2.78) (I
think, anyway < 3). It too relies on matrix multiplication.
Standard parsing methods are O(n**3) or worse and tend to be
recursive; however, the constant factor for the "faster"
algorithm is huge. On the other hand...would that change for a
parallel system? Or one especially designed for matrix
multiplication?

> I do not feel that recursion and/or tail-recursion are bad things. They
> are extremely powerful tools, and like all tools (especially extremely
> powerful ones) they must be used with care. But, if you think about it,
> just about any programming language tool must be used with care.

No disagreement here. Though I'd emphasize that the care required
with recursion is of a slightly different kind than for most
other control structures. When I write a while loop, for
example, the care is that I get the algorithm right; the cost of
a while loop is usually evident.  When I code recursively, not
only must I get the algorithm right, but I must also consider
the space and time consumed by the recursive calls.  This
requires a significantly more complex analysis than a while
usually requires.

The power of recursion is a good reason for emphasizing it in
programming courses. As long as they also teach techniques for
estimating the cost of the tool.  And grind it into the
student's head that these cost estimation techniques are
necessary.