Lisp: recorrer la lista y reemplazar valores
Frecuentes
Visto 8,443 equipos
5
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))
3 Respuestas
4
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
3
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
3
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 common-lisp or haz tu propia pregunta.
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. - SomeKittens
usar NTH es la peor manera posible. - Rainer Joswig
@SomeKittens Impresionante, hombre :D - dev1223