Могут ли массивы SBCL иметь типизированные массивы в качестве элементов?

Рассмотрим этот простой пример:

(deftype image nil '(simple-array single-float (100)))

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

(defparameter tmp
  (make-array 100
              :element-type 'single-float
              :initial-element 0.0))

Давайте проверим тип на всякий случай:

CL-USER> (type-of tmp)
(SIMPLE-ARRAY SINGLE-FLOAT (100))

Все хорошо. Давайте посмотрим, могли бы мы иметь эти маленькие массивы в другом массиве, чтобы упростить поиск, вместо того, чтобы помещать все в одномерный массив и иметь головную боль при расчете индексов доступа.

(defparameter image-array
  (make-array 10
              :element-type 'image
              :initial-element tmp))

Нет никакого способа, которым это потерпит неудачу, но проверка на всякий случай:

CL-USER> (type-of image-array)
(SIMPLE-VECTOR 10)

К сожалению, это совсем не то, что мы хотим. Похоже, этот новый массив по умолчанию имеет тип элемента по умолчанию:

CL-USER> (array-element-type image-array)
T

Это, вероятно, означает, что теперь приложению придется проверять не только элементы массива контейнеров, но и элементы дочерних массивов со всеми последствиями для производительности. Возникают следующие вопросы:

Возможно ли в SBCL хранить типизированные массивы как элементы массива в другом массиве?

РЕДАКТИРОВАТЬ: Это может быть слишком рано, чтобы паниковать, хотя, поскольку это возвращает правильный тип:

CL-USER> (type-of (aref image-array 0))
(SIMPLE-ARRAY SINGLE-FLOAT (100))

В таком случае, почему мы получаем T как тип элемента из (array-element-type image-array)?

2 ответа

Решение

Немного предыстории того, что на самом деле означает ТИП ЭЛЕМЕНТА

Если вы даете тип элемента MAKE-ARRAY вы просите реализацию Common Lisp создать массив с оптимизированным пространственным макетом (!), который может быть ограничен определенными типами элементов. Вам не нужно получать массив именно для этого типа элемента, но массив, который наиболее эффективен в данной реализации для этого типа элемента.

  • для чисел реализация может иметь специальные версии для битов, 8-битные байты, 16-битные слова, 32-битные слова и еще несколько.

  • он может иметь специальные версии для массивов символов, таких как строки

  • он может иметь специальные версии для одного или нескольких типов чисел с плавающей точкой

Есть ли еще, зависит от реализации, которую вы используете.

Для любого типа элемента, который не имеет специальной реализации, тип элемента обновляется до T, Это означает, что массив может иметь все виды объектов в качестве элементов, а более крупные элементы, такие как массивы, строки, структуры, объекты CLOS,... всегда будут храниться как указатель на объект в куче.

Несколько примеров для определенной реализации:

Целые

CL-USER> (upgraded-array-element-type '(integer 0 1))
(UNSIGNED-BYTE 1)                                                                                                                                                 
CL-USER> (upgraded-array-element-type '(integer 0 2))
(UNSIGNED-BYTE 2)                                                                                                                                                 
CL-USER> (upgraded-array-element-type '(integer 0 3))
(UNSIGNED-BYTE 2)                                                                                                                                                 
CL-USER> (upgraded-array-element-type '(integer 0 4))
(UNSIGNED-BYTE 4)                                                                                                                                                 
CL-USER> (upgraded-array-element-type '(integer 0 5))
(UNSIGNED-BYTE 4)                                                                                                                                                 
CL-USER> (upgraded-array-element-type '(integer 0 7))
(UNSIGNED-BYTE 4)                                                                                                                                                 
CL-USER> (upgraded-array-element-type '(integer 0 8))
(UNSIGNED-BYTE 4)                                                                                                                                                 
CL-USER> (upgraded-array-element-type '(integer 0 15))
(UNSIGNED-BYTE 4)                                                                                                                                                 
CL-USER> (upgraded-array-element-type '(integer 0 16))
(UNSIGNED-BYTE 8)                                                                                                                                                 
CL-USER> (upgraded-array-element-type '(integer 0 256))
(UNSIGNED-BYTE 16)                                                                                                                                                
CL-USER> (upgraded-array-element-type '(integer 0 4423423))
(UNSIGNED-BYTE 32)                                                                                                                                                
CL-USER> (upgraded-array-element-type '(integer 0 4423423423423))
(UNSIGNED-BYTE 64)                                                                                                                                                
CL-USER> (upgraded-array-element-type '(integer 0 4423423423423423423423423423423))
T      

Персонажи

CL-USER> (upgraded-array-element-type 'character)
CHARACTER

Поплавки

CL-USER> (upgraded-array-element-type 'single-float)
SINGLE-FLOAT                                                                                                                                                      
CL-USER> (upgraded-array-element-type 'long-float)
DOUBLE-FLOAT

массив

CL-USER> (upgraded-array-element-type 'array)
T     

Даже если вы попросите более конкретные версии массивов в качестве элементов, вы, скорее всего, получите T как ответ.

Когда запрашивать специальный массив

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

Но: операции с массивами со специальными типами элементов могут быть медленнее. Во время выполнения в безопасном коде может происходить дополнительная проверка типа, и операции по изменению / чтению элементов могут потребовать более медленных инструкций процессора.

Ограничения массивов Common Lisp

Таким образом, Common Lisp не имеет оптимизированного макета хранилища для массивов структур, векторов, объектов CLOS и т. Д. Поскольку для каждого хранимого элемента есть указатель, доступ всегда требует косвенного обращения, и нет ничего, что гарантировало бы, что эти объекты хранятся в линейном виде. порядок в памяти. В линейном порядке хранятся указатели на них в массиве.

Проверьте вашу реализацию, имеет ли она оптимизированный макет пространства для массивов с плавающей точкой (одинарный, двойной, длинный, ...).

Многомерные массивы

Common Lisp поддерживает истинные многомерные массивы с ARRAY-RANK-LIMIT (ABCL имеет максимум 8 измерений на моем ARM, некоторые другие реализации поддерживают больше измерений). Эти многомерные массивы также могут иметь специализированные типы элементов.

Похоже на проблему XY: вам лучше использовать многомерный массив с плавающей точкой:

(make-array (list width height) ...)

... тогда вы делаете (aref matrix row column) и вам не нужно вычислять индексы. Когда вы храните массив внутри массива, вам все равно нужно сохранять метаданные, связанные с каждым массивом, например, тип его элементов, потому что вы можете ссылаться на каждый массив из другого места. Поэтому в основном массиве хранятся только ссылки, а не необработанные числа с плавающей точкой.

Также обратите внимание, что тип, который может храниться в массиве, может быть супертипом объявленного типа из-за обновления массива: System Class ARRAY.

Другие вопросы по тегам