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

问题描述 投票:4回答:5

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出现的内容中深入了解这个问题。我怀疑答案是以某种形式存在的......)

arrays common-lisp sbcl
5个回答
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,它只是保持符号+len+


5
投票

我对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))

它完美无缺。


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
投票

另外两个答案告诉你how to work around the limitationwhere the limitation is specified in the standard

我会试着告诉你这个限制来自哪里。

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

int N = 10;
int v[N];

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


1
投票

好吧,因为似乎没有人真正给出提问者需要的内容,这里是:

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

#.是一个标准的readmacro,用于评估读取时间的值。 Lisp表只看到扩展的内容。 http://www.lispworks.com/documentation/HyperSpec/Body/02_dhf.htm

© www.soinside.com 2019 - 2024. All rights reserved.