LISP: mejora la recursión a la recursión de cola en la función de rotación de lista
Frecuentes
Visto 2,807 veces
2
I have a recursive function in LISP to rotate list to right or left as following:
(If the number is positive- push to the left, if it's negative - push to the right)
> (rotate-n ‘(5 6 7 8 9) -3)
(7 8 9 5 6)
> (rotate-n ‘(5 6 7 8 9) 3)
(8 9 5 6 7)
La función:
(defun rotate-n (L n)
(cond ((= n 0) L)
((> n 0) (rotate-n (rotate-left L) (- n 1)))
((< n 0) (rotate-n (rotate-right L) (+ n 1)))))
Is there a way to get the same result using tail-recursion? The functions rotate-right and rotate-left work fine.
EDIT: I confused. The function I wrote above was tail-recursion. If I am not wrong, this function is regular recursion for the same purpose:
(defun rotate-n-r (L n)
(cond ((= n 0) L)
((> n 0) (rotate-left (rotate-n-r L (- n 1))))
((< n 0) (rotate-right (rotate-n-r L (+ n 1))))))
1 Respuestas
1
Here is a tail-recursive function which does what you want:
(defun rotate-list (list n)
(cond ((plusp n)
(rotate-list
(append (rest list) (list (first list)))
(1- n)))
((minusp n)
(rotate-list
(append (last list) (butlast list))
(1+ n)))
(t list)))
Note that it conses (i.e., allocates memory) a lot:
(ext:time (rotate-list '(5 6 7 8 9) 30))
Permanent Temporary
Class instances bytes instances bytes
----- --------- --------- --------- ---------
CONS 5 80 145 2320
----- --------- --------- --------- ---------
Total 5 80 145 2320
Real time: 3.2E-5 sec.
Run time: 0.0 sec.
Space: 2400 Bytes
(5 6 7 8 9)
A non-consing version:
(defun nrotate-list (list n )
(cond ((plusp n)
(nrotate-list
(nconc (rest list) (progn (setf (cdr list) nil) list))
(1- n)))
((minusp n)
(nrotate-list
(nconc (last list) (nbutlast list))
(1+ n)))
(t list)))
does not allocate anything while still being tail-recursive:
(time (nrotate-list '(5 6 7 8 9) 30))
Real time: 2.3E-5 sec.
Run time: 0.0 sec.
Space: 0 Bytes
(5 6 7 8 9)
Note that both versions are pretty stupid performance-wise
(they scan the list dos veces para cada iteration, i.e.,
their time complexity is O(n*length(list))
).
The efficient version would scan the list just una vez (i.e., time complexity O(length(list))
) y la voluntad no be recursive.
Respondido el 01 de enero de 13 a las 18:01
No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas recursion functional-programming lisp rotation tail-recursion or haz tu propia pregunta.
Do you have your rotate-left and -right functions available? Because they'll have to be modified/included in rotate-n for tail recursion to work. - Nathaniel Ford
Sure, I wrote them and they work. Do you think it is possible to solve it with tail-recursion? - זאבי כהן
Yes, but I'm not sure it is a good idea: the effect is exactly the same either way. This method is pretty much tail recursion: you rotate before you make the recursive call. - Nathaniel Ford
Why you think current implementation isn't tail recursive? Last call in cond is
rotate-n
- paul@paul has the right of it here. Unless you mean you want your rotate-left and -right functions to be tail recursive, which may or may not already be the case. - Nathaniel Ford