LISP - 专用向量
Lisp - 专用向量
Section titled “Lisp - 专用向量”标准的 Lisp 向量是一种通用容器,可以容纳任何类型的 Lisp 对象。这种灵活性是有代价的:每个元素都需要为类型标签和指针分配存储空间,这一概念被称为装箱(boxing)。相比之下,专用向量(specialized vector)经过优化,仅存储一种特定的原始类型元素。这在性能和内存使用方面具有显著优势。
为何使用专用向量?
Section titled “为何使用专用向量?”- 内存效率:通过直接存储原始数据值而不进行装箱(例如,存储原始的 8 位整数而非指向整数对象的指针),专用向量显著减少内存使用。
- 性能提升:访问元素速度快得多。CPU 可以直接处理数据,提高缓存局部性,并使编译器能够进行强大的 SIMD(单指令多数据)优化。
- 类型安全:Lisp 系统强制执行类型限制。尝试存储错误类型的元素会发出错误信号,从而及早发现错误。
- 外部函数接口(FFI):它们对于与用 C 等其他语言编写的库进行接口至关重要,因为它们直接映射到内存中的 C 风格数组。
创建和使用专用向量
Section titled “创建和使用专用向量”您可以通过为 make-array 提供 :element-type 参数来创建专用向量。系统将自动选择尽可能紧凑的表示形式。
您可以创建各种整数大小的向量。例如,(unsigned-byte 8) 非常适合存储字节。
;; 创建一个用于存储 5 个整数的向量,每个整数都是一个无符号字节(0-255)。;; 它用零初始化。(let ((byte-vector (make-array 5 :element-type '(unsigned-byte 8)))) (print byte-vector) ;; -> #(0 0 0 0 0)
;; 赋值 (setf (aref byte-vector 0) 10) (setf (aref byte-vector 1) 255) (print byte-vector) ;; -> #(10 255 0 0 0)
;; 这将引发类型错误,因为 300 超出了 unsigned-byte 8 的范围。 ;; (setf (aref byte-vector 2) 300))位向量(Bit-Vector)
Section titled “位向量(Bit-Vector)”位向量是存储布尔值(0 或 1)序列的极其紧凑的方式。Lisp 提供了一个方便的读取器宏 #*,用于字面创建它们。
;; 使用读取器宏创建位向量(let ((bit-v #*10110)) (print bit-v) ;; -> #*10110
;; 访问元素 (print (aref bit-v 2)) ;; -> 1
;; 修改元素 (setf (aref bit-v 2) 0) (print bit-v)) ;; -> #*10010字符向量(字符串)
Section titled “字符向量(字符串)”根据定义,Lisp 的 string(字符串)是一种专用的一维数组,其元素类型为 character(字符)。
;; 这些是等效的(defvar string1 "Hello")(defvar string2 (make-array 5 :element-type 'character :initial-contents '(#\H #\e #\l #\l #\o)))
(print (aref string1 1)) ; -> #\e专用浮点向量对于科学和数值计算至关重要。
;; 创建一个单精度浮点数向量。(let ((float-vec (make-array 3 :element-type 'single-float))) (setf (aref float-vec 0) 3.14f0) (setf (aref float-vec 1) 1.618f0) (print float-vec));; -> #(3.14 1.618 0.0)何时使用专用向量
Section titled “何时使用专用向量”在以下情况时选择专用向量:
- 处理大量同构的原始数据(例如,数字、字符)。
- 编写对内存带宽和缓存性能要求较高的性能关键代码(例如,游戏开发、数据分析、图像处理)。
- 处理二进制数据,例如从文件或网络套接字读取。
- 与 C 或其他系统级库进行接口。
对于灵活性比原始性能更重要的通用编程,或者当您需要存储不同对象类型的混合时,标准向量通常是更方便的选择。