[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Tail Recursion in Common Lisp ???



    Date: Thu, 17 Jul 1986  22:00 EDT
    From: STEVER%OZ.AI.MIT.EDU@XX.LCS.MIT.EDU

	Date: Thursday, 17 July 1986  10:53-EDT
	From: Bernard S. Greenberg <BSG at STONY-BROOK.SCRC.Symbolics.COM>

	If the spec -allows- tail-recursion optimization, and "my"
	implementation "supports" tail-recursion, then "I" can ... write a
	"tail-recursive" program, which ...  will blow the stack of most
	certified implementations that do not "support" tail-recursion,
	and is thus, subtly, not portable.

    Which is why, even though tail recursion is an attribute of the
    implementation, it might be nice for the standard to say something on
    the issue (either a "yes, you can assume it" or "no, you can't.")
    A lot of people, at least at MIT, are learning Lisp via SCHEME, so
    they come out into the real world believing that tail recursion is a
    safe way to do things.

There are some implementations that will drop out if they are forced to
implement tail recursion.  The standard argument revolves around
programming environments and debugger information.

    Since I'm relatively new to the list, I'm not familiar with discussion
    of iteration constructs at all.  I sat down to write a quickie Common Lisp
    program which used tail recursion, and was rather surprised when it
    blew up.  The last form in the body was an IF, in which both branches
    called the toplevel function (I had hoped) tail-recursively.

    Can this kind of iteration be \nicely/ expressed other ways?
    Zetalisp's LOOP was able to give me what I wanted, expressed in a very
    different way.  But since it's not part of the standard (and I dislike
    its non-LISPish syntax), I'd rather not use it.

    A rough example of the kind of thing I was doing:
	    (defun remove (atom list)
	       (labels ((remove-1 (remaining result)
			  (cond ((endp remaining) result)
				((eq (first remaining) atom)
				 (remove-1 (cdr remaining) result))
				(t (remove-1 (cdr remaining)
					     (cons (first remaining)
						   result))))))
		       (nreverse (remove-1 list '()))))

Try
	(defun remove (atom list)
	  (do ((rev-answer nil (if (eql (first remaining) atom)
				   rev-answer
				   (cons (first remaining) rev-answer)))
	       (remaining list (cdr remaining)))
	      ((endp remaining) (nreverse rev-answer))))

Somebody made the claim the other day, I think on this list, that if you
had tail recursion you can do away with the "syntactic sugar" of
iteration constructs.  I'll claim that is somewhat bogus.  The iteration
constructs express an idea.  They express iteration.  Syntactic sugar
embodies ideas and provides abstractions.  Also, the iteration
constructs can easily macroexpand into tail recursive calls if that's
the way you want to implement them.