How to convert a list to a one dimensional array (vector) in Common Lisp having the same output either using make-array or coerce?

Viewed 215

I want to convert from a simple list:

'(1 2 3)

To a one dimensional array (vector):

#(1 2 3)

I found this question which is similar but does not cover this problem.

I also managed to find a way to achieve it. Not sure if it is the best:

CL-USER> (coerce '(1 2 3) 'vector)
#(1 2 3)
CL-USER> (type-of *)
(SIMPLE-VECTOR 3)

I wonder if it is possible to use make-array to achieve the same result. I tried:


CL-USER> (make-array '() :initial-contents '(1 2 3)) 
#0A(1 2 3)
CL-USER> (type-of *)
(SIMPLE-ARRAY T NIL)

This is close, but I do not understand #0A in #0A(1 2 3).

For some reason that I do not understand, this output does not work on further compositions, such as aref:

CL-USER> (aref #0A(1 2 3) 0)
; Evaluation aborted on #<SIMPLE-ERROR "Wrong number of subscripts, ~W, for array of rank ~W." {1003C07793}>.

Is it possible to use make-array to achieve the same result as coerce? What are the differences between these two approaches? Is one of them faster or more elegant than the other?

1 Answers

COERCE is fine, you can even specify the element-type if you want:

USER> (coerce '(1 2 3) '(vector fixnum))
#(1 2 3)
USER> (describe *)
#(1 2 3)
  [simple specialized vector]

Element-type: FIXNUM
Length: 3

You can do the same with make-array, but you need to give the right dimension. The dimension parameter indicates how many rows, columns, etc. the possibly multi-dimensional array has. It should be a list, but when you only have one dimension, it can just be a number. Both forms here are equivalent:

USER> (make-array 3 :initial-contents '(1 2 3))
#(1 2 3)

USER> (make-array '(3) :initial-contents '(1 2 3))
#(1 2 3)

Usually you would call (length list). The added value compared to coerce is that you can specify the :fill-pointer or :adjustable arguments, something that the type argument of coerce cannot convey.

The printed representation of an array is #nA(), where n is the number of dimensions of the array, where the number and A is omitted if its a vector (n = 1). For example:

USER> (make-array '(10 2) :initial-element 0)
#2A((0 0) (0 0) (0 0) (0 0) (0 0) (0 0) (0 0) (0 0) (0 0) (0 0))

For a given array of dimension n, you access the elements with aref and as many arguments as there are dimensions, in row-major order (the same order you specified the dimensions in make-array).

USER> (aref * 5 1)
0 (0 bits, #x0, #o0, #b0)

In your example, you defined an array of dimension 0, because you wrote '() (a.k.a. just () or nil) as the dimension. This can be used if you need a box to store a single element:

USER> (defparameter *box* (make-array nil :element-type '(mod 8)))
*BOX*
USER> (describe *box*)
#0A0
  [simple specialized array]

Element-type: (UNSIGNED-BYTE 4)
Dimensions: NIL
Storage vector: #<(SIMPLE-ARRAY (UNSIGNED-BYTE 4) (1)) {101EA0C59F}>
; No values

USER> (setf (aref *box*) 7)
7 (3 bits, #x7, #o7, #b111)

USER> (incf (aref *box*))
8 (4 bits, #x8, #o10, #b1000)

USER> (aref *box*)
8 (4 bits, #x8, #o10, #b1000)

(as you can see, the values that can be stored in the array are the ones corresponding to the upgraded-element-type, here (unsigned-byte 4); for example, (setf (aref *box*) (expt 2 16)) signals an error)

It looks like :initial-contents had the same result as :initial-element, because with your example the content of the zero-dimensional array was the list (1 2 3)

Is one of them faster or more elegant than the other?

I try to use coerce when possible because it's shorter to write and it looks more descriptive of what I am doing. I don't think it is faster, unless you know in advance the length of the list.

Related