如何在sbcl(或常见的lisp)的向量中指定元素类型?

use*_*284 3 types sbcl common-lisp

我尝试在sbcl 1.1.14中使用代码,但似乎类型检查忽略了vector元素的声明.

(defun test (vec)
  (declare (type (vector integer) vec))
  (format nil "~a~&" (elt vec 0)))
Run Code Online (Sandbox Code Playgroud)

任何提示?谢谢!

Rai*_*wig 12

首先请注意,将声明作为类型检查不是标准ANSI Common Lisp提供的.这是CMUCL推出的扩展.SBCL是CMUCL的后代.

Common Lisp类型系统适用于向量和数组的方式有点不同寻常.

元素类型.

正在使用元素类型创建数组.这意味着Lisp系统将创建一个可以存储该类型元素的数组.但Common Lisp并不要求每个元素类型都有专门的数组版本.如果数组的特定版本不可用,则元素类型将升级为下一个"较大"类型.

元素类型升级示例

CL-USER 14 > (upgraded-array-element-type '(unsigned-byte 1))
(UNSIGNED-BYTE 1)

CL-USER 15 > (upgraded-array-element-type '(unsigned-byte 2))
(UNSIGNED-BYTE 2)
Run Code Online (Sandbox Code Playgroud)

因此有针对(unsigned-byte 1)和优化的阵列版本(unsigned-byte 2).

CL-USER 16 > (upgraded-array-element-type '(unsigned-byte 3))
(UNSIGNED-BYTE 4)
Run Code Online (Sandbox Code Playgroud)

OOPS!没有优化的阵列(unsigned-byte 3).如果你请求这样一个数组,你会得到一个更大的数组(unsigned-byte 4).

CL-USER 17 > (upgraded-array-element-type '(unsigned-byte 4))
(UNSIGNED-BYTE 4)

CL-USER 18 > (upgraded-array-element-type '(unsigned-byte 5))
(UNSIGNED-BYTE 8)

CL-USER 19 > (upgraded-array-element-type 'integer)
T
Run Code Online (Sandbox Code Playgroud)

上图显示整数没有特殊数组.你会得到一个通用数组.

你的守则

(defun test (vec)
  (declare (type (vector integer) vec))
  (format nil "~a~&" (elt vec 0)))
Run Code Online (Sandbox Code Playgroud)

所以你的声明真的意味着:

变量vec绑定到一个可以保存integer数字的向量.

这并不意味着:

变量vec绑定到一个只保存integer数字的向量.

CL-USER 21 > (typep '#(a "b" #\c) '(vector integer))
T
Run Code Online (Sandbox Code Playgroud)

以上在我的Lisp中返回true,因为向量是一般向量,它可以存储整数.因此它会检查向量的类型,但它并不关心向量的内容是否实际上都是整数类型.它只是说,向量COULD包含整数.

基于谓词的类型检查

Common Lisp允许类型声明使用谓词.

CL-USER 28 > (defun vector-of-numbers-p (vector)
               (and (typep vector 'vector)
                    (every 'integerp vector)))
VECTOR-OF-NUMBERS-P

CL-USER 29 > (typep '#(a "b" #\c) '(satisfies arrayp))
T

CL-USER 30 > (typep '#(a "b" #\c) '(satisfies vector-of-numbers-p))
NIL

CL-USER 31 > (typep '#(1 2 3) '(satisfies vector-of-numbers-p))
T
Run Code Online (Sandbox Code Playgroud)

但是在编译时检查一下?可能不是.