18

The only thing I've found that works is

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

but that seems far too complicated to be the 'right' way.

Drew
  • 75,699
  • 9
  • 109
  • 225
Sean Allred
  • 6,861
  • 16
  • 85

5 Answers5

19

Use cl-map, instead:

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

A little extra background: cl-map is the Common Lisp map function that generalizes to sequence types:

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

It can also convert between sequence types (eg, here, the input is a list and the output is a vector):

(cl-map 'vector #'1+ '(1 2 3 4)) ;; ==> [2 3 4 5]
Dan
  • 32,584
  • 6
  • 98
  • 168
  • 1
    18 seconds the 'winner' :) Don't the `cl` libraries give compiler warnings, though? (Mostly because the FSF is obnoxious?) – Sean Allred Nov 01 '14 at 14:51
  • 1
    FWIW, I think the byte compilation problems were related to the old `cl` library rather than the rejiggered `cl-lib` library. I don't for example, get any warnings when I `(defun fnx () (cl-map 'vector #'1+ '[1 2 3 4]))` and then `(byte-compile 'fnx)`. – Dan Nov 01 '14 at 15:01
  • 2
    Even if you use the compatibility cl-lib, I think you'll get warnings on older emacs (24.2). I wouldn't worry about it though, you have to pick your battles. – Malabarba Nov 01 '14 at 15:07
  • Note that this dynamically creates a list, then converts that to a vector and discards the temporary list. – Clément Apr 13 '20 at 22:05
15

Since I was beat by 18 seconds, here's a simpler and safer way to do it without the cl library. It also doesn't evaluate the elements.

(apply #'vector (mapcar #'1+ [1 2 3 4])) ;; => [2 3 4 5]
Malabarba
  • 22,878
  • 6
  • 78
  • 163
  • That's also quite nice! Re: your earlier comment about older Emacs: it seems especially helpful if you have to anticipate legacy users. It seems most helpful if you only have to use it in a couple of spots, at which point you can trade off the slight inconvenience against avoiding the `cl-lib` dependency. – Dan Nov 01 '14 at 15:27
  • 1
    Very nifty!! I didn't think about using `apply`. – Sean Allred Nov 01 '14 at 22:51
  • I think `(apply #'vector ...)` might be ever so slightly faster, but for completeness, it can also be replaced with `(vconcat ...)`. – Basil Nov 12 '18 at 20:03
2

The not so elegant inplace-variant for the case that the original vector is no longer needed afterwards and memory allocation is time-critical (e.g. the vector is big).

(setq x [1 2 3 4])

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

The result is stored in x. If you need the form to return x in the end you can add finally return x as follows:

(cl-loop for var across-ref x do
         (setf var (1+ var))
         finally return x)
Tobias
  • 32,569
  • 1
  • 34
  • 75
2

For completeness, using seq:

(require 'seq)
(seq-into (seq-map #'1+ [1 2 3 4]) 'vector)
Sean Allred
  • 6,861
  • 16
  • 85
  • There is a deleted answer from [Fólkvangr](https://emacs.stackexchange.com/users/19761/f%c3%b3lkvangr) 2018-11-12 with the exact same `seq-into` line. The user has deleted his answer for the following reason: " My solution is less relevant because the seq library uses the underlying Common Lisp extensions. – Fólkvangr May 16 at 8:53" – Tobias Jun 19 '19 at 07:28
  • @Tobias I guess I'd disagree with that logic. Everything is going to end up using vconcat or vector anyway, but the different interface paradigms are useful to have on record. – Sean Allred Jun 19 '19 at 10:31
  • No problem. I just saw the deleted answer of Fólkvangr (almost) matching yours and wanted to notify you. For whatever reason seeing deleted answers requires 10000 rep:-(. – Tobias Jun 19 '19 at 11:02
  • @Tobias yeah, I never really understood why those privileges were site-specific :-) – Sean Allred Jun 19 '19 at 11:07
1

You can use loop

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

Sometimes you don't want to modify the original vector, you can make a copy

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

or create a new vector from scratch

(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
  • 14,302
  • 1
  • 18
  • 39