为什么常量不能在Common Lisp类型说明符中用作数组维度?

时间:2013-09-30 03:19:39

标签: arrays common-lisp sbcl

Common Lisp的至少一些实现不允许在某些类型说明符中将用户定义的常量用作数组维度。例如,在SBCL中,此代码:

(defconstant +len+ 3)

(defun foo (x) 
  (declare (type (simple-array fixnum (+len+)) x))
  x)

生成此错误:

; in: DEFUN FOO
;     (TYPE (SIMPLE-ARRAY FIXNUM (+LEN+)) X)
; 
; caught ERROR:
;   bad dimension in array type: +LEN+

为什么呢?似乎令人惊讶的是,用户定义的常量不能在类型说明符中使用,因为希望能够使用某种全局定义来协调多个类型说明符。我知道类型说明符需要在编译时完全可以理解。但我认为编译器能够用defconstant替换用它们的文字值定义的符号。我原以为这是defconstant的目的之一。 (到目前为止,我还没有成功地从Common Lisp Hyperspec,CLTL2,SBCL手册或者Google出现的内容中深入了解这个问题。我怀疑答案是以某种形式存在的......)

5 个答案:

答案 0 :(得分:5)

我想像这样的东西会起作用:

(defconstant +len+ 3)

(deftype len-3-fixnum-array () `(array fixnum (,+len+)))

(defun foo (x)
  (declare (type len-3-fixnum-array x))
  (print x))

(foo (make-array 3 :element-type 'fixnum))
;; #(0 0 0)

(foo (make-array 4 :element-type 'fixnum))
;; The value #(0 0 0 0) is not of type (VECTOR FIXNUM 3).
;;    [Condition of type TYPE-ERROR]

但是,您需要记住,类型注释只是建议,编译器可以完全忽略它们。


即。问题不在于你不能在declare形式中使用常量,它就像它被引用一样,所以你的常量不会计算为3,它只保留符号{{1 }}

答案 1 :(得分:4)

如果查看ANSI CL规范,可以明确定义类型的语法。对于simple-array

simple-array [{element-type | *} [dimension-spec]]

dimension-spec::= rank | * | ({dimension | *}*) 

其中:

dimension---a valid array dimension.

element-type---a type specifier.

rank---a non-negative fixnum.

然后将valid array dimension解释为fixnum。

simple-array类型声明中没有变量或常量标识符。类型声明具有自己的语法,不进行评估或类似。它们是类型表达式,可以出现在特殊位置的代码中( - >声明)。

您可以使用DEFTYPE创建新的类型名称并使用它们。

答案 2 :(得分:4)

我对2D数组有同样的问题:

(defconstant board-width  4)
(defconstant board-height 3)

(setq *board* (make-array '(board-width board-height) :initial-element 0))

我总是收到错误

The value BOARD-WITH is not of type SB-INT:INDEX.

所以我用这种方式改变了最后一行:

(setq *board* (make-array (list board-width board-height) :initial-element 0))

它完美无缺。

答案 3 :(得分:2)

其他两个答案会告诉您how to work around the limitationwhere the limitation is specified in the standard

我会尽力告诉你这个限制来自哪里

原因是Lisp声明不仅仅是 human 阅读器的代码文档,而且还是编译器在优化代码时的帮助。也就是说,数组大小在声明时应该允许编译器在必要时分配固定大小的数组,并推断数组索引的存储要求。它类似于禁止像

这样的代码的C限制
int N = 10;
int v[N];

当您开始查看编译器POV的声明时,要求将变得非常自然。

答案 4 :(得分:1)

好吧,因为似乎没有人真正给出提问者的需求,所以这里是:

(defun foo (x) 
  (declare (type (simple-array fixnum (#.+len+)) x))
  x)

#.是一个标准的读取宏,它可以在读取时间内评估该值。 Lisp表单只能看到扩展的内容。 http://www.lispworks.com/documentation/HyperSpec/Body/02_dhf.htm