Lisp: recorrer la lista y reemplazar valores

En este problema, tengo tres listas (estructuradas de forma idéntica). Dos tienen todos los números y el otro está lleno de nil. Estoy tratando de reemplazar el valor correspondiente en la lista vacía con la adición de los valores correspondientes de las dos listas. Lo que tengo hasta ahora utiliza un bucle y utiliza setf para reemplazar el valor.

(defun add-two-lists (list1 list2 list3)
   (loop for a in list1
        for b in list2
        for c in list3 do
        (setf c (+ a b))))

El problema es que esta función no está siendo destructiva. ¿Cómo hago que esta función sea destructiva?


Ok, soy consciente de que podría usar un apply para hacer esto, pero para fines futuros o tangenciales, ¿hay alguna manera de usar un bucle para hacer lo mismo?


He decidido recurrir a mi penúltima solución; utilice la longitud de la lista para atravesar las listas.

(defun add-two-lists (list1 list2 list3)
       (loop for x from 0 to (- (list-length list1) 1) do
            (setf (nth x list3) (+ (nth x list1) (nth x list2))))
       (values list3))

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

Mi método favorito para hacer una función destructiva: la lleno de visiones de grandeza, la convenzo de que libere su ira, dejando que el odio fluya a través de ella. Por lo general, funciona, a menos que haya una función secundaria que lo devuelva al lado de la luz. -

usar NTH es la peor manera posible. -

@SomeKittens Impresionante, hombre :D -

3 Respuestas

He aquí una forma:

(defun add-two-lists (list1 list2 list3)
   (loop for a in list1
        for b in list2
        for c on list3 do
        (rplaca c (+ a b)))

APÉNDICE

Aquí hay otra forma que usa map en lugar de loop:

(defun add-two-lists (list1 list2 list3)
  (mapl #'(lambda (cl al bl) (rplaca cl (+ (car al) (car bl))))
    list3 list1 list2))

Respondido el 12 de junio de 12 a las 20:06

is rplaca ¿parte del ceceo común? y en que se diferencia de setf? - Zchpyvr

Sí, rplaca es parte de Common Lisp (setf usa rplaca cuando el lugar que está modificando es el auto de un contras): lispworks.com/documentation/lw51/CLHS/Body/f_rplaca.htm -- se podría decir (setf (car c)...) en vez de (rplaca c...) - Doug Currie

Otra forma más de hacer lo mismo sin usar un bucle (aunque es conceptualmente similar)

(defun add-two-lists (a b c &optional (d c))
  (if a
    (add-two-lists
     (cdr a) (cdr b)
     (cdr (rplaca c (+ (car a) (car b)))) d) d))

(add-two-lists '(1 2 3 4 5) '(1 2 3 4 5) '(nil nil nil nil nil))

EDITAR

(defun add-two-lists (a b c &optional (d c))
  (if a
    (add-two-lists
     (cdr a) (cdr b)
     (cdr (rplaca c (+ (car a) (car b)))) d) d))

(time
 (dotimes (i 1e6)
   (add-two-lists '(1 2 3 4 5)
          '(1 2 3 4 5)
          '(nil nil nil nil nil))))

;; Evaluation took:
;;   0.077 seconds of real time
;;   0.076004 seconds of total run time (0.076004 user, 0.000000 system)
;;   98.70% CPU
;;   214,723,476 processor cycles
;;   0 bytes consed

(defun add-two-lists-1 (list1 list2 list3)
  (loop for a in list1
     for b in list2
     for c on list3 do
       (rplaca c (+ a b))))

(time
 (dotimes (i 1e6)
   (add-two-lists-1 '(1 2 3 4 5)
          '(1 2 3 4 5)
          '(nil nil nil nil nil))))

;; Evaluation took:
;;   0.060 seconds of real time
;;   0.060004 seconds of total run time (0.060004 user, 0.000000 system)
;;   100.00% CPU
;;   169,395,444 processor cycles
;;   0 bytes consed

EDIT 2

Pero observe el comportamiento de la versión optimizada. Posiblemente, nuevamente, YMMV, pero esto es lo que obtengo en Debian de 64 bits con SBCL.

(defun add-two-lists (a b c &optional (d c))
  (declare (optimize (speed 3) (safety 0)))
  (declare (type list a b c d))
  (if a
    (add-two-lists
     (cdr a) (cdr b)
     (cdr (rplaca
       c 
       (the fixnum
         (+ (the fixnum (car a))
        (the fixnum (car b)))))) d) d))

(time
 (dotimes (i 1e6)
   (add-two-lists '(1 2 3 4 5)
          '(1 2 3 4 5)
          '(nil nil nil nil nil))))

;; Evaluation took:
;;   0.041 seconds of real time
;;   0.040002 seconds of total run time (0.040002 user, 0.000000 system)
;;   97.56% CPU
;;   114,176,175 processor cycles
;;   0 bytes consed

(defun add-two-lists-1 (list1 list2 list3)
  (declare (optimize (speed 3) (safety 0)))
  (loop for a fixnum in list1
     for b fixnum in list2
     for c cons on list3 do
       (rplaca c (the fixnum (+ a b)))))

(time
 (dotimes (i 1e6)
   (add-two-lists-1 '(1 2 3 4 5)
          '(1 2 3 4 5)
          '(nil nil nil nil nil))))

;; Evaluation took:
;;   0.040 seconds of real time
;;   0.040003 seconds of total run time (0.040003 user, 0.000000 system)
;;   100.00% CPU
;;   112,032,123 processor cycles
;;   0 bytes consed

Respondido el 12 de junio de 12 a las 19:06

Pensé en esta solución recursiva como último recurso. Me preocupa la velocidad en este caso, por lo que dudo en elegir una solución recursiva en lugar de una solución iterativa (posible, aún por encontrar). - Zchpyvr

Hmm, supongo que hay poca diferencia en el tiempo. A modo de comparación, ¿sabe si el código actualizado es más lento o más rápido que sus funciones? - Zchpyvr

Hay poca necesidad de agregar tipos al código. La mayor parte del manejo de listas es 'lo suficientemente rápido' si lo hace bien. También tenga en cuenta que en la mayoría de los compiladores Lisp esto generará un código no seguro con SAFETY = 0. Eso es muy malo. - Rainer Joswig

Common Lisp proporciona una función para eso: MAP-EN.

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

Desafortunadamente, map-into no funciona con listas anidadas, que es con lo que estoy tratando. (Sé que mi código anterior no tiene en cuenta las listas anidadas, pero lo tengo extendido a una función recursiva en mi archivo) Si sabe de alguna manera, podría utilizar map-into a listas anidadas, por favor muéstrame. - Zchpyvr

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.