Mar*_*ars 4 arrays sbcl common-lisp
Common Lisp的至少某些实现不允许在某些类型说明符中将用户定义的常量用作数组维。例如,在SBCL中,此代码:
(defconstant +len+ 3)
(defun foo (x)
(declare (type (simple-array fixnum (+len+)) x))
x)
Run Code Online (Sandbox Code Playgroud)
产生此错误:
; in: DEFUN FOO
; (TYPE (SIMPLE-ARRAY FIXNUM (+LEN+)) X)
;
; caught ERROR:
; bad dimension in array type: +LEN+
Run Code Online (Sandbox Code Playgroud)
为什么?不能在类型说明符中使用用户定义的常量似乎令人惊讶,因为希望能够使用某种全局定义来协调多个类型说明符。我知道类型说明符在编译时需要完全可理解。但是我本以为编译器将能够用defconstant其文字值替换定义的符号。我本以为这是的目的之一defconstant。(到目前为止,我未能从Common Lisp Hyperspec,CLTL2,SBCL手册或Google提出的内容中加深对这个问题的理解。我怀疑答案以某种形式存在。)
小智 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]
Run Code Online (Sandbox Code Playgroud)
但是,您需要记住,类型注释仅是建议,允许编译器完全忽略它们。
也就是说,问题不在于您不能在declare表单中使用常量,而是因为它的作用就像被引用一样,因此您的常量不会求值为3,而是仅保留符号+len+。
如果您查看 ANSI CL 规范,就会清楚地定义类型的语法。对于简单数组:
simple-array [{element-type | *} [dimension-spec]]
dimension-spec::= rank | * | ({dimension | *}*)
Run Code Online (Sandbox Code Playgroud)
在哪里:
dimension---a valid array dimension.
element-type---a type specifier.
rank---a non-negative fixnum.
Run Code Online (Sandbox Code Playgroud)
所述valid array dimension然后作为一个Fixnum说明。
simple-array类型声明中没有变量或常量标识符。类型声明有自己的语法,并且不会被评估或类似。它们是可以出现在代码中特殊位置(-> 声明)的类型表达式。
不过,您可以使用DEFTYPE来创建新的类型名称并使用它们。
我对2D数组有同样的问题:
(defconstant board-width 4)
(defconstant board-height 3)
(setq *board* (make-array '(board-width board-height) :initial-element 0))
Run Code Online (Sandbox Code Playgroud)
我总是出错
The value BOARD-WITH is not of type SB-INT:INDEX.
Run Code Online (Sandbox Code Playgroud)
因此,我以这种方式更改了最后一行:
(setq *board* (make-array (list board-width board-height) :initial-element 0))
Run Code Online (Sandbox Code Playgroud)
而且效果很好。