考虑这个简单的例子:
(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)
作为元素类型?
答案 0 :(得分:5)
ELEMENT TYPE实际含义的背景
如果将元素类型赋予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对象等数组的优化存储布局。由于存储了每个元素的指针,访问总是需要间接,并且没有任何东西可以保证这些对象是以线性顺序存储在内存中。以线性顺序存储的是数组中指向它们的指针。
检查您的实现是否已针对float(single,double,long,...)数组优化了空间布局。
多维数组
Common Lisp支持最多ARRAY-RANK-LIMIT
的真正多维数组(ABCL在我的ARM上有8个维度,其他一些实现支持更多维度)。这些多维数组也可以具有专门的元素类型。
答案 1 :(得分:4)
看起来像一个XY问题:你最好使用多维浮点数组:
(make-array (list width height) ...)
...然后你做(aref matrix row column)
,你不必计算指数。将数组存储在数组中时,仍然需要保留与每个数组关联的元数据,例如其元素的类型,因为您可以从其他位置引用每个数组。这就是为什么主数组只存储引用而不是原始浮点数。
另请注意,由于数组升级,可以存储在数组中的类型可以是声明类型的超类型:System Class ARRAY。