Как я могу нанести на карту вектор и получить вектор?

15

Единственное, что я нашел, это работает

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

но кажется , что далеко слишком сложно , чтобы быть «правильный» путь.

Шон Оллред
источник

Ответы:

19

Используйте cl-mapвместо этого:

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

Немного дополнительного фона: cl-mapэто функция Common Lisp,map которая обобщает типы последовательностей:

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

Он также может преобразовывать типы последовательностей (например, здесь вход является списком, а выход - вектором):

(cl-map 'vector #'1+ '(1 2 3 4)) ;; ==> [2 3 4 5]
Дэн
источник
1
18 секунд - победитель :) Разве clбиблиотеки не выдают предупреждения компилятора? (Главным образом потому, что ФСФ противна?)
Шон Оллред
1
FWIW, я думаю, что проблемы с байтовой компиляцией были связаны со старой clбиблиотекой, а не с перенастроенной cl-libбиблиотекой. Я, например, не получаю никаких предупреждений, когда я, (defun fnx () (cl-map 'vector #'1+ '[1 2 3 4]))а затем (byte-compile 'fnx).
Дан
2
Даже если вы используете cl-lib для совместимости, я думаю, вы получите предупреждения о старых emacs (24.2). Я бы не волновался об этом, ты должен выбрать свои сражения.
Малабарба
16

Поскольку меня избили 18 секунд, вот более простой и безопасный способ сделать это без библиотеки cl. Это также не оценивает элементы.

(apply #'vector (mapcar #'1+ [1 2 3 4])) ;; => [2 3 4 5]
Malabarba
источник
Это тоже довольно приятно! Re: ваш предыдущий комментарий о старых Emacs: он кажется особенно полезным, если вы ожидаете старых пользователей. Это кажется наиболее полезным, если вам нужно использовать его только в нескольких местах, после чего вы можете обойтись без незначительных неудобств и избежать cl-libзависимости.
Дан
1
Очень изящно !! Я не думал об использовании apply.
Шон Оллред
Я думаю, что (apply #'vector ...)может быть немного быстрее, но для полноты его также можно заменить на (vconcat ...).
Василий
1

Не очень элегантный вариант на месте для случая, когда исходный вектор больше не нужен, а распределение памяти критично ко времени (например, вектор большой).

(setq x [1 2 3 4])

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

Результат сохраняется в x. Если вам нужна форма для возврата xв конце, вы можете добавить finally return xследующее:

(cl-loop for var across-ref x do
         (setf var (1+ var))
         finally return x)
Тобиас
источник
1

Для полноты, используя seq:

(require 'seq)
(seq-into (seq-map #'1+ [1 2 3 4]) 'vector)
Шон Оллред
источник
От Fólkvangr 2018-11-12 есть удаленный ответ с точно такой же seq-intoстрокой. Пользователь удалил свой ответ по следующей причине: «Мое решение менее актуально, потому что библиотека seq использует базовые расширения Common Lisp. - Fólkvangr 16 мая в 8:53»
Тобиас
@ Тобиас Я думаю, я бы не согласился с этой логикой. В любом случае все закончится использованием vconcat или vector, но различные парадигмы интерфейса полезно иметь при записи.
Шон Оллред
Нет проблем. Я только что увидел удаленный ответ Fólkvangr (почти) совпадающий с вашим и хотел уведомить вас. По какой-либо причине для просмотра удаленных ответов требуется 10000 представителей :-(.
Тобиас
@Tobias да, я так и не понял, почему эти привилегии были
Шон Оллред
0

Вы можете использовать цикл

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

Иногда вы не хотите изменять исходный вектор, вы можете сделать копию

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

или создайте новый вектор с нуля

(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
источник