Comment puis-je mapper sur un vecteur et obtenir un vecteur?

15

La seule chose que j'ai trouvée qui fonctionne est

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

mais cela semble beaucoup trop compliqué pour être la «bonne» façon.

Sean Allred
la source

Réponses:

19

Utilisez cl-mapplutôt:

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

Un petit arrière-plan supplémentaire: cl-mapest la fonction Common Lispmap qui se généralise aux types de séquence:

(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"

Il peut également convertir entre les types de séquence (par exemple, ici, l'entrée est une liste et la sortie est un vecteur):

(cl-map 'vector #'1+ '(1 2 3 4)) ;; ==> [2 3 4 5]
Dan
la source
1
18 secondes le «gagnant» :) Les clbibliothèques ne donnent-elles pas d'avertissements au compilateur, cependant? (Surtout parce que la FSF est odieuse?)
Sean Allred
1
FWIW, je pense que les problèmes de compilation d'octets étaient liés à l'ancienne clbibliothèque plutôt qu'à la cl-libbibliothèque relancée . Je ne suis pas, par exemple, obtenir des avertissements quand je (defun fnx () (cl-map 'vector #'1+ '[1 2 3 4]))puis (byte-compile 'fnx).
Dan
2
Même si vous utilisez la compatibilité cl-lib, je pense que vous obtiendrez des avertissements sur les anciens emacs (24.2). Je ne m'en inquiéterais pas cependant, vous devez choisir vos batailles.
Malabarba
16

Depuis que j'ai été battu de 18 secondes, voici un moyen plus simple et plus sûr de le faire sans la bibliothèque cl. Il n'évalue pas non plus les éléments.

(apply #'vector (mapcar #'1+ [1 2 3 4])) ;; => [2 3 4 5]
Malabarba
la source
C'est aussi assez sympa! Re: votre commentaire précédent sur les Emacs plus anciens: cela semble particulièrement utile si vous devez anticiper les utilisateurs hérités. Cela semble plus utile si vous ne devez l'utiliser que dans quelques endroits, à quel point vous pouvez échanger le léger inconvénient contre éviter la cl-libdépendance.
Dan
1
Très chouette !! Je n'ai pas pensé à utiliser apply.
Sean Allred
Je pense que cela (apply #'vector ...)pourrait être un peu plus rapide, mais pour être complet, il peut également être remplacé par (vconcat ...).
Basil
1

La variante inplace pas si élégante pour le cas où le vecteur d'origine n'est plus nécessaire par la suite et l'allocation de mémoire est critique en temps (par exemple, le vecteur est grand).

(setq x [1 2 3 4])

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

Le résultat est stocké dans x. Si vous avez besoin du formulaire pour retourner xà la fin, vous pouvez ajouter finally return xcomme suit:

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

Pour être complet, utilisez seq:

(require 'seq)
(seq-into (seq-map #'1+ [1 2 3 4]) 'vector)
Sean Allred
la source
Il y a une réponse supprimée de Fólkvangr 2018-11-12 avec exactement la même seq-intoligne. L'utilisateur a supprimé sa réponse pour la raison suivante: "Ma solution est moins pertinente car la bibliothèque seq utilise les extensions Common Lisp sous-jacentes. - Fólkvangr 16 mai à 8:53"
Tobias
@Tobias Je suppose que je ne suis pas d'accord avec cette logique. Tout va finir par utiliser vconcat ou vector de toute façon, mais les différents paradigmes d'interface sont utiles à enregistrer.
Sean Allred
Aucun problème. Je viens de voir la réponse supprimée de Fólkvangr (presque) correspondant à la vôtre et je voulais vous en informer. Pour une raison quelconque, voir les réponses supprimées nécessite 10000 répétitions :-(.
Tobias
@Tobias ouais, je n'ai jamais vraiment compris pourquoi ces privilèges étaient spécifiques au site :-)
Sean Allred
0

Vous pouvez utiliser la boucle

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

Parfois, vous ne voulez pas modifier le vecteur d'origine, vous pouvez en faire une copie

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

ou créez un nouveau vecteur à partir de zéro

(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])
xuchunyang
la source