Как правильно указать тип элемента регулируемого вектора - PullRequest
2 голосов
/ 05 апреля 2019

Ниже приведена структура, предназначенная для функционирования либо в качестве (lifo) стека, либо (fifo) очереди

(defstruct jvector
  (vector (make-array 0 :adjustable t :fill-pointer 0) :type (array * (*)))
  (start 0 :type (integer 0 *)))

с содержимым в диапазоне от jvector-start до jvector-vector fill-pointer. Я хотел бы иметь возможность указать тип элемента содержимого с чем-то вроде

(defun create-jvector (&key (element-type t))
  (make-jvector :vector (make-array 0 :element-type element-type :adjustable t :fill-pointer 0)
                :start 0))

и нажимные элементы с

(defun push-jvector (elt jvec)
  (vector-push-extend elt (jvector-vector jvec)))

Однако тип элемента в create-jvector игнорируется. Например,

* (defparameter v (create-jvector :element-type 'integer))
V
* v
#S(JVECTOR :VECTOR #() :START 0)
* (push-jvector 1 v)
0          ;OK result
* v
#S(JVECTOR :VECTOR #(1) :START 0)
* (push-jvector 'a v)
1          ;not OK result
* v
#S(JVECTOR :VECTOR #(1 A) :START 0)

Почему нажатие ‘a не приводит к ошибке типа, и что бы это исправить?

Ответы [ 2 ]

6 голосов
/ 05 апреля 2019

Если вы укажете MAKE-ARRAY тип элемента, он попытается выделить массив с эффективным использованием пространства. Реализации обычно поддерживают несколько вариантов:

Примеры:

CL-USER 13 > (mapcar #'upgraded-array-element-type
                     '(bit fixnum character))
((UNSIGNED-BYTE 1) (SIGNED-BYTE 64) CHARACTER)

Но для многих типов не существует пространственно-эффективных массивов:

CL-USER 14 > (mapcar #'upgraded-array-element-type
                     '(integer string standard-object))
(T T T)

Вы запросили массив целых чисел и получили общий массив: см. Тип T, возвращаемый upgraded-array-element-type.

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

4 голосов
/ 05 апреля 2019

Почему нет ошибки типа

Тип проверяется в соответствии с array-element-type массива:

(defparameter w (create-jvector :element-type 'fixnum))

(array-element-type (jvector-vector v))
=> T

(array-element-type (jvector-vector w))
=> FIXNUM

Нажатие символа на W приводит к ошибке. Как сказал jkiiski, тип используется главным образом, чтобы помочь компилятору иметь возможность использовать специализированные представления для массивов (битвекторы, строки, ...).

integer может быть bignum, поэтому тип обновленного элемента T:

(upgraded-array-element-type 'integer)
=> T

Как решить вашу проблему

Это решение намекает jkiiski, а именно добавить тип в структуру; здесь я также переопределяю конструктор непосредственно из структуры:

(defstruct (j2vector
             (:constructor make-jvector
                           (element-type
                            &aux
                            (start 0)
                            (vector (make-array 0
                                                :adjustable t
                                                :fill-pointer 0
                                                :element-type element-type)))))
  element-type
  vector
  (start 0 :type (integer 0)))

Затем вы явно проверяете тип:

(defun push-jvector (elt jvec)
  (assert (typep elt (j2vector-element-type jvec)) ())
  (vector-push-extend elt (j2vector-vector jvec)))
...