Jak zmapować wektor i uzyskać wektor?

15

Jedyne, co znalazłem, to działa

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

ale to wydaje daleko zbyt skomplikowane, aby być „prawo” sposób.

Sean Allred
źródło

Odpowiedzi:

19

cl-mapZamiast tego użyj :

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

Trochę ekstra tła: cl-mapjest Common Lisp mapfunkcja , która uogólnia typów sekwencji:

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

Może także konwertować między typami sekwencji (np. Tutaj dane wejściowe to lista, a dane wyjściowe to wektor):

(cl-map 'vector #'1+ '(1 2 3 4)) ;; ==> [2 3 4 5]
Dan
źródło
1
18 sekund „zwycięzca” :) Czy clbiblioteki nie ostrzegają jednak kompilatora? (Głównie dlatego, że FSF jest wstrętny?)
Sean Allred
1
FWIW, myślę, że problemy z kompilacją bajtów były związane ze starą clbiblioteką, a nie cl-libbiblioteką zmienioną . Na przykład nie otrzymuję żadnych ostrzeżeń, kiedy ja (defun fnx () (cl-map 'vector #'1+ '[1 2 3 4]))i wtedy (byte-compile 'fnx).
Dan
2
Nawet jeśli użyjesz kompatybilności cl-lib, myślę, że dostaniesz ostrzeżenia na starszych emacsach (24.2). Nie martwię się o to, musisz wybrać swoje bitwy.
Malabarba
16

Ponieważ zostałem pokonany przez 18 sekund, oto prostszy i bezpieczniejszy sposób na zrobienie tego bez biblioteki cl. Nie ocenia też elementów.

(apply #'vector (mapcar #'1+ [1 2 3 4])) ;; => [2 3 4 5]
Malabarba
źródło
To też całkiem miłe! Re: twój wcześniejszy komentarz na temat starszych Emacsa: wydaje się szczególnie pomocny, jeśli musisz przewidzieć starszych użytkowników. Wydaje się to najbardziej pomocne, jeśli musisz użyć go tylko w kilku miejscach, w którym to momencie możesz odłożyć drobną niedogodność, aby uniknąć cl-libzależności.
Dan
1
Bardzo fajne !! Nie myślałem o użyciu apply.
Sean Allred
Myślę, że (apply #'vector ...)może być jeszcze trochę szybszy, ale dla kompletności można go również zastąpić (vconcat ...).
Basil,
1

Niezbyt elegancki wariant lokalny dla przypadku, gdy oryginalny wektor nie jest już potrzebny, a przydział pamięci jest krytyczny czasowo (np. Wektor jest duży).

(setq x [1 2 3 4])

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

Wynik jest przechowywany w x. Jeśli potrzebujesz formularza do powrotu x, możesz dodać finally return xw następujący sposób:

(cl-loop for var across-ref x do
         (setf var (1+ var))
         finally return x)
Tobiasz
źródło
1

Dla kompletności, używając seq:

(require 'seq)
(seq-into (seq-map #'1+ [1 2 3 4]) 'vector)
Sean Allred
źródło
Istnieje usunięta odpowiedź z Fólkvangr 12.12.2018 z dokładnie tą samą seq-intolinią. Użytkownik usunął swoją odpowiedź z następującego powodu: „Moje rozwiązanie jest mniej istotne, ponieważ biblioteka seq korzysta z podstawowych rozszerzeń Common Lisp. - Fólkvangr 16 maja o 8:53”
Tobias
@Tobias Chyba nie zgadzam się z tą logiką. Wszystko i tak skończy się na użyciu vconcat lub wektora, ale różne paradygmaty interfejsu są przydatne do zapisania.
Sean Allred
Nie ma problemu. Właśnie zobaczyłem usuniętą odpowiedź Fólkvangr (prawie) pasującą do twojej i chciałem cię powiadomić. Z jakiegokolwiek powodu zobaczenie usuniętych odpowiedzi wymaga 10000 powtórzeń :-(.
Tobias
@Tobias, tak, nigdy tak naprawdę nie zrozumiałem, dlaczego te przywileje były specyficzne dla witryny :-)
Sean Allred
0

Możesz użyć pętli

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

Czasami nie chcesz modyfikować oryginalnego wektora, możesz wykonać kopię

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

lub utwórz nowy wektor od zera

(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
źródło