unofficial mirror of help-gnu-emacs@gnu.org
 help / color / mirror / Atom feed
* tail recursion hack in Emacs Lisp?
@ 2004-07-16 15:07 Oliver Scholz
  2004-07-16 16:23 ` Oliver Scholz
  0 siblings, 1 reply; 5+ messages in thread
From: Oliver Scholz @ 2004-07-16 15:07 UTC (permalink / raw)


I like tail recursion a lot, because I regard it as a means to specify
the flow of control that is IMO the right way for /some/ problems.
Thus tail call reduction is on the top of my personal wish list for
enhancements of Emacs Lisp---even before closures and greater speed.

I have been wondering for a while, whether it would be possible to
fake tail recursion without hacking the byte code compiler.  Say, if
we have an `iterate' macro like in CMU CL (which in turn is inspired
by Scheme's named let): would it be possible that the macroexpanded
code would be non-recursive Emacs Lisp code?

Today I got bored in my lunch break and came up with this.

(iterate fact ((n n)
	       (r 1))
  (if (= n 1)
      r
    (fact (1- n) (* n r))))

should expand to:

(let ((continue t)
      (result (list n 1)))
  (while continue
    (setq result
	  (catch 'repeat
	    (setq result
		  (apply
		   (lambda (n r)
		     (if (= n 1)
			 r
		       (throw 'repeat (list (1- n) (* n r)))))
		   result))
	    (setq continue nil)
	    result)))
  result)


(Of course, a code walker would be necessary.) I can't see any
problems with that approach right know. But lacking experience in
implementing Lisp, I might be missing something.

So my question is: Can anybody think of a case where this approach
would break?

While I am at it: I quickly hacked something together:

(defmacro iterate (name arglist &rest body)
  (let ((catch-symbol (make-symbol "--repeat"))
	(continue (make-symbol "--continue"))
	(result (make-symbol "--result"))
	(lambda-list (mapcar 'car arglist))
	(initial-args (mapcar 'cadr arglist)))
    (setq body (iterate-code-walk body name catch-symbol))
    `(let ((,continue t)
	   (,result (list ,@initial-args)))
       (while ,continue
	 (setq ,result
	       (catch ',catch-symbol
		 (setq ,result
		       (apply
			(lambda ,lambda-list
			  ,@body)
			,result))
		 (setq ,continue nil)
		 ,result)))
       ,result)))

(defun iterate-code-walk (exp sym catch-sym)
  (cond ((and (listp exp)
	      (eq (car exp) 'quote))
	 exp)
	((and (listp exp)
	      (eq (car exp) sym))
	 (list 'throw `(quote ,catch-sym)
	       (cons 'list (cdr exp))))
	((listp exp)
	 (iterate-code-walk-list exp sym catch-sym))
	(t exp)))

(defun iterate-code-walk-list (exp sym catch-sym)
  (mapcar (lambda (el)
	    (iterate-code-walk el sym catch-sym))
	  exp))       


    Oliver
-- 
29 Messidor an 212 de la Révolution
Liberté, Egalité, Fraternité!

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: tail recursion hack in Emacs Lisp?
  2004-07-16 15:07 tail recursion hack in Emacs Lisp? Oliver Scholz
@ 2004-07-16 16:23 ` Oliver Scholz
  2004-07-16 16:42   ` Oliver Scholz
  2004-07-16 16:53   ` Stefan Monnier
  0 siblings, 2 replies; 5+ messages in thread
From: Oliver Scholz @ 2004-07-16 16:23 UTC (permalink / raw)


Oliver Scholz <alkibiades@gmx.de> writes:

[`iterate' macro with code-walker]
>
> So my question is: Can anybody think of a case where this approach
> would break?

Silly me. The usual case for breaking such things would apply:

(iterate fact ((n 10)
	       (r 1))
  (if (= n 1)
      r
    (funcall (intern "fact") (1- n) (* n r))))

Not to mention `eval'.

:-(  :-(  :-(


    Oliver
-- 
29 Messidor an 212 de la Révolution
Liberté, Egalité, Fraternité!

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: tail recursion hack in Emacs Lisp?
  2004-07-16 16:23 ` Oliver Scholz
@ 2004-07-16 16:42   ` Oliver Scholz
  2004-07-16 16:53   ` Stefan Monnier
  1 sibling, 0 replies; 5+ messages in thread
From: Oliver Scholz @ 2004-07-16 16:42 UTC (permalink / raw)


[Sorry for talking to myself today.]

Oliver Scholz <alkibiades@gmx.de> writes:

> Oliver Scholz <alkibiades@gmx.de> writes:
>
> [`iterate' macro with code-walker]
>>
>> So my question is: Can anybody think of a case where this approach
>> would break?
>
> Silly me. The usual case for breaking such things would apply:
>
> (iterate fact ((n 10)
> 	       (r 1))
>   (if (= n 1)
>       r
>     (funcall (intern "fact") (1- n) (* n r))))
>
> Not to mention `eval'.

And of course that could be circumwented by `fset'ing NAME:

(defmacro iterate (name arglist &rest body)
  (let ((catch-symbol (make-symbol "--repeat"))
	(continue (make-symbol "--continue"))
	(result (make-symbol "--result"))
	(fdef (make-symbol "--fdef"))
	(lambda-list (mapcar 'car arglist))
	(initial-args (mapcar 'cadr arglist)))
    `(let ((,fdef (and (fboundp ',name)
			   (symbol-function ',name))))
       (unwind-protect
	   (let ((,continue t)
		 (,result (list ,@initial-args)))
	     (fset ',name (lambda (&rest args)
			    (throw ',catch-symbol args)))
	     (while ,continue
	       (setq ,result
		     (catch ',catch-symbol
		       (setq ,result
			     (apply
			      (lambda ,lambda-list
				,@body)
			      ,result))
		       (setq ,continue nil)
		       ,result)))
	     ,result)
	 (if (null ,fdef)
	     (fmakunbound ',name)
	   (fset ',name ,fdef))))))


Yet, I dislike it more and more.


    Oliver
-- 
29 Messidor an 212 de la Révolution
Liberté, Egalité, Fraternité!

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: tail recursion hack in Emacs Lisp?
  2004-07-16 16:23 ` Oliver Scholz
  2004-07-16 16:42   ` Oliver Scholz
@ 2004-07-16 16:53   ` Stefan Monnier
  2004-07-16 17:37     ` Oliver Scholz
  1 sibling, 1 reply; 5+ messages in thread
From: Stefan Monnier @ 2004-07-16 16:53 UTC (permalink / raw)


> So my question is: Can anybody think of a case where this approach
> would break?

I have another question: why is it better than CL's `do' ?


        Stefan

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: tail recursion hack in Emacs Lisp?
  2004-07-16 16:53   ` Stefan Monnier
@ 2004-07-16 17:37     ` Oliver Scholz
  0 siblings, 0 replies; 5+ messages in thread
From: Oliver Scholz @ 2004-07-16 17:37 UTC (permalink / raw)


Stefan Monnier <monnier@iro.umontreal.ca> writes:

>> So my question is: Can anybody think of a case where this approach
>> would break?
>
> I have another question: why is it better than CL's `do' ?

Huh?  You mean like:

(do ((n 10 (1- n))
     (r 1 (* n r)))
    ((= n 1) r))

I had no intention to suggest that it were better. iterate/named
let is just different. Personally I find `do' hard to read, but
that might be a matter of taste.

I used `fact' just because it is the classical example. I can't
think of a really good example right now, exept maybe this:

(defun my-flatten-list (lst)
  (iterate walk-list ((lst lst)
		      (acc nil)
		      (stk nil))
    (if (null lst)
	(if (null stk)
	    (nreverse acc)
	  (walk-list stk acc nil))
      (if (listp (car lst))
	  (walk-list (car lst)
		     acc
		     (if (null (cdr lst))
			 stk
		       (cons (cdr lst)
			     stk)))
	(walk-list (cdr lst)
		   (cons (car lst) acc)
		   stk)))))

There might be better examples. To quote the CMU CL manual: "The
main advantage of using iterate over do is that iterate naturally
allows stepping to be done differently depending on conditionals
in the body of the loop."

    Oliver
-- 
29 Messidor an 212 de la Révolution
Liberté, Egalité, Fraternité!

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2004-07-16 17:37 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2004-07-16 15:07 tail recursion hack in Emacs Lisp? Oliver Scholz
2004-07-16 16:23 ` Oliver Scholz
2004-07-16 16:42   ` Oliver Scholz
2004-07-16 16:53   ` Stefan Monnier
2004-07-16 17:37     ` Oliver Scholz

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).