¿Cómo puedo mapear sobre un vector y obtener un vector?


15

Lo único que he encontrado que funciona es

(eval `(vector ,@(mapcar #'1+ [1 2 3 4])))
=> [2 3 4 5]

pero que parece ahora demasiado complicado para ser la forma 'correcta'.

Respuestas:


19

Use cl-map, en cambio:

(cl-map 'vector #'1+ [1 2 3 4])

Un poco de información adicional: cl-mapes la función Common Lispmap que se generaliza a los tipos de secuencia:

(cl-map 'vector #'1+ '[1 2 3 4]) ;; ==> [2 3 4 5]
(cl-map 'list   #'1+ '(1 2 3 4)) ;; ==> (2 3 4 5)
(cl-map 'string #'upcase "abc")  ;; ==> "ABC"

También puede convertir entre tipos de secuencia (por ejemplo, aquí, la entrada es una lista y la salida es un vector):

(cl-map 'vector #'1+ '(1 2 3 4)) ;; ==> [2 3 4 5]

1
18 segundos el 'ganador' :) Sin clembargo, ¿las bibliotecas no dan advertencias al compilador? (¿Principalmente porque la FSF es desagradable?)
— Sean Allred el

1
FWIW, creo que los problemas de compilación de bytes estaban relacionados con la clbiblioteca anterior en lugar de la cl-libbiblioteca rejigger . Por ejemplo, no recibo ninguna advertencia cuando (defun fnx () (cl-map 'vector #'1+ '[1 2 3 4]))y luego (byte-compile 'fnx).
— Dan

2
Incluso si usa la compatibilidad cl-lib, creo que recibirá advertencias en emacs anteriores (24.2). Sin embargo, no me preocuparía, tienes que elegir tus batallas.
— Malabarba

16

Como me vencieron 18 segundos, aquí hay una manera más simple y segura de hacerlo sin la biblioteca cl. Tampoco evalúa los elementos.

(apply #'vector (mapcar #'1+ [1 2 3 4])) ;; => [2 3 4 5]

¡Eso también es bastante bueno! Re: su comentario anterior sobre Emacs anteriores: parece especialmente útil si tiene que anticiparse a los usuarios heredados. Parece más útil si solo tiene que usarlo en un par de lugares, momento en el que puede compensar el pequeño inconveniente de evitar la cl-libdependencia.
— Dan

1
Muy ingenioso !! No pensé en usar apply.
— Sean Allred

Creo que (apply #'vector ...)podría ser un poco más rápido, pero para completar, también se puede reemplazar (vconcat ...).
— Basil

1

La variante in situ no tan elegante para el caso de que el vector original ya no sea necesario después y la asignación de memoria es crítica en el tiempo (por ejemplo, el vector es grande).

(setq x [1 2 3 4])

(cl-loop for var across-ref x do
         (setf var (1+ var)))

El resultado se almacena en x. Si necesita que el formulario regrese xal final, puede agregar finally return xlo siguiente:

(cl-loop for var across-ref x do
         (setf var (1+ var))
         finally return x)

1

Para completar, usando seq:

(require 'seq)
(seq-into (seq-map #'1+ [1 2 3 4]) 'vector)

Hay una respuesta eliminada de Fólkvangr 2018-11-12 con exactamente la misma seq-intolínea. El usuario ha eliminado su respuesta por la siguiente razón: "Mi solución es menos relevante porque la biblioteca seq utiliza las extensiones subyacentes de Common Lisp. - Fólkvangr 16 de mayo a las 8:53"
— Tobias

@Tobias, creo que no estoy de acuerdo con esa lógica. De todos modos, todo terminará usando vconcat o vector, pero los diferentes paradigmas de interfaz son útiles para tener en el registro.
— Sean Allred

No hay problema. Acabo de ver la respuesta eliminada de Fólkvangr (casi) coincidente con la tuya y quería notificarte. Por alguna razón, ver las respuestas eliminadas requiere 10000 rep :-(.
— Tobias

@Tobias, sí, nunca entendí por qué esos privilegios eran específicos del sitio :-)
— Sean Allred

0

Puedes usar loop

(let ((v (vector 1 2 3 4)))
  (dotimes (i (length v))
    (aset v i (1+ (aref v i))))
  v)
;; => [2 3 4 5]

A veces no desea modificar el vector original, puede hacer una copia

(let* ((v0 (vector 1 2 3 4))
       (v (copy-sequence v0)))
  (dotimes (i (length v))
    (aset v i (1+ (aref v i))))
  (list v0 v))
;; => ([1 2 3 4] [2 3 4 5])

o crear un nuevo vector desde cero

(let* ((v0 (vector 1 2 3 4))
       (v (make-vector (length v0) nil)))
  (dotimes (i (length v))
    (aset v i (1+ (aref v0 i))))
  (list v0 v))
;; => ([1 2 3 4] [2 3 4 5])
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.