LISP: mejora la recursión a la recursión de cola en la función de rotación de lista

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))))))

preguntado el 12 de junio de 12 a las 21:06

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. -

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. -

Why you think current implementation isn't tail recursive? Last call in cond is rotate-n -

@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. -

1 Respuestas

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 or haz tu propia pregunta.