SBCL阵列可以将类型化数组作为元素吗?

时间:2017-08-07 15:50:25

标签: arrays multidimensional-array lisp common-lisp 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 个答案:

答案 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