Subject: Re: Q: tail-recursive procedure to generate all permutations of a list From: Erik Naggum <erik@naggum.net> Date: 2000/10/04 Newsgroups: comp.lang.lisp Message-ID: <3179671668484915@naggum.net> * The Glauber <theglauber@my-deja.com> | I'm a novice in Lisp, so bear with me if this is a stupid question. | I've been playing with generating all permutations of a list. | I wrote the following, based on a similar function by Daniel Nesmith: | | (defun permute-list (l) | "Returns a list of all permutations of the input list (list of lists)." | (if (< (length l) 2) | (list l) | ; insert the first element in each position of | ; the permutation of the other elements of the | ; original list | (mapcan | #'(lambda (seq) | (let ((res-list nil)) | (dotimes (i (length l) res-list) | (push | (nconc (subseq seq 0 i) | (cons (first l) (subseq seq i))) | res-list)))) | (permute-list (rest l))))) I tend to favor a slight different approach: (defun permutations (list) (if (cdr list) (loop with rotation = list do (setq rotation (nconc (last rotation) (nbutlast rotation))) nconc (loop for list in (permutations (rest rotation)) collect (cons (first rotation) (copy-list list))) until (eq rotation list)) (list list))) It is about 20 times faster with an 8-element list to permute, but as it turns out, it is significantly more memory-intensive than your approach (4.4 times). (Measured with Allegro CL 6.0.) | Are there any obvious optimizations i missed? That (setq rotation (nconc (last rotation) (nbutlast rotation))) eventually returns the original list and that it can thus be used in a recursive function to avoid duplicating the control structre is not obvious (according to a colleague I showed this to :). That particular expression may also be optimized to traverse the list only once, if you really have to. (For a 10-element list, 98% of the time is spent in garbage collection.) | Is there any way to make this tail-recursive? More interesting than this unnatural obsession with tail-recursion is how deep your recursion is relative to the length of the input. If you can't immediately tell, you need to spend more time with recursion and completely forget tail-recursion until you can get a sufficiently good "gut feeling" for maximal recursion depths. | The next step, i guess, would be to write a macro | with-all-permutations, which would take a list and a block, and run | that block once for each of the permutations of the list. But i | can't see how to do that without first going through the whole | process of generating all the permutations... Pass in a function call, wrap the macro body in a lambda, and off you go. Remember to do this only at the top-level call, and default to some innocuous function like identity. #:Erik -- If this is not what you expected, please alter your expectations.