sbcl Common Lisp incf 警告
sbcl Common Lisp incf warning
我正在学习 lisp 教程,他们编写了以下代码
(set 'x 11)
(incf x 10)
并且解释器给出了以下错误:
; in: INCF X
; (SETQ X #:NEW671)
;
; caught WARNING:
; undefined variable: X
;
; compilation unit finished
; Undefined variable:
; X
; caught 1 WARNING condition
21
递增 x 的正确方法是什么?
这确实是您要递增 x
的方式,或者至少是一种递增方式。然而,这不是您要绑定 x
的方式。在 CL 中,您需要在使用名称之前为名称建立绑定,而您不能仅通过分配给它来实现。因此,例如,这段代码(在新的 CL 图像中)是不合法的 CL:
(defun bad ()
(setf y 2))
通常这会导致编译时警告和 运行 时错误,尽管它可能会执行其他操作:其行为未定义。
你所做的,特别是,实际上比这更糟糕:你已经将一个值添加到 x
的 symbol-value
中(使用 set
,这样做),然后假设像 (incf x)
这样的东西会起作用,这是极不可能的。例如考虑这样的事情:
(defun worse ()
(let ((x 2))
(set 'x 4)
(incf x)
(values x (symbol-value 'x))))
这是(与 bad
不同)的合法代码,但它可能不会执行您希望它执行的操作。
许多 CL 实现 do 允许在顶层分配给先前未绑定的变量,因为在对话环境中这很方便。但是这些赋值的确切含义超出了语言标准。
CMUCL 及其衍生产品(包括 SBCL)在历史上一直比当时的其他实现更加认真。我认为这是因为解释器比大多数其他解释器更认真 and/or 他们秘密地编译了所有东西,然后编译器把东西捡起来了。
另一个问题是 CL 对于顶级变量的语义有点尴尬:如果你努力以正常方式建立顶级绑定,与 defvar
& 朋友,那么你也会导致变量是 special——动态作用域——这是一个普遍的影响:它使该名称的 all 绑定变得特殊。这通常是一个非常不受欢迎的后果。 CL 作为一种语言,没有顶级 词法 变量的概念。
因此,许多实现所做的是对某些东西的顶层绑定有某种非正式的概念,这并不意味着特殊的声明:如果你只是在顶层说 (setf x 3)
那么这不会传染整个环境。但是随之而来的是各种尴尬的问题:这样做之后,例如(symbol-value 'x)
的结果是什么?
幸运的是 CL 是一种强大的语言,很可能 定义 语言中的顶级词法变量。这是一个非常 hacky 的实现,叫做 deflexical
。请注意,那里有更好的实现(至少包括我的一个,我现在找不到):这并不是一个万无一失的解决方案。
(defmacro deflexical (var &optional value)
;; Define a cheap-and-nasty global lexical variable. In this
;; implementation, global lexicals are not boundp and the global
;; lexical value is not stored in the symbol-value of the symbol.
;;
;; This implementation is *not* properly thought-through and is
;; without question problematic
`(progn
(define-symbol-macro ,var (get ',var 'lexical-value))
(let ((flag (cons nil nil)))
;; assign a value only if there is not one already, like DEFVAR
(when (eq (get ',var 'lexical-value flag) flag)
(setf (get ',var 'lexical-value) ,value))
;; Return the symbol
',var)))
我正在学习 lisp 教程,他们编写了以下代码
(set 'x 11)
(incf x 10)
并且解释器给出了以下错误:
; in: INCF X
; (SETQ X #:NEW671)
;
; caught WARNING:
; undefined variable: X
;
; compilation unit finished
; Undefined variable:
; X
; caught 1 WARNING condition
21
递增 x 的正确方法是什么?
这确实是您要递增 x
的方式,或者至少是一种递增方式。然而,这不是您要绑定 x
的方式。在 CL 中,您需要在使用名称之前为名称建立绑定,而您不能仅通过分配给它来实现。因此,例如,这段代码(在新的 CL 图像中)是不合法的 CL:
(defun bad ()
(setf y 2))
通常这会导致编译时警告和 运行 时错误,尽管它可能会执行其他操作:其行为未定义。
你所做的,特别是,实际上比这更糟糕:你已经将一个值添加到 x
的 symbol-value
中(使用 set
,这样做),然后假设像 (incf x)
这样的东西会起作用,这是极不可能的。例如考虑这样的事情:
(defun worse ()
(let ((x 2))
(set 'x 4)
(incf x)
(values x (symbol-value 'x))))
这是(与 bad
不同)的合法代码,但它可能不会执行您希望它执行的操作。
许多 CL 实现 do 允许在顶层分配给先前未绑定的变量,因为在对话环境中这很方便。但是这些赋值的确切含义超出了语言标准。
CMUCL 及其衍生产品(包括 SBCL)在历史上一直比当时的其他实现更加认真。我认为这是因为解释器比大多数其他解释器更认真 and/or 他们秘密地编译了所有东西,然后编译器把东西捡起来了。
另一个问题是 CL 对于顶级变量的语义有点尴尬:如果你努力以正常方式建立顶级绑定,与 defvar
& 朋友,那么你也会导致变量是 special——动态作用域——这是一个普遍的影响:它使该名称的 all 绑定变得特殊。这通常是一个非常不受欢迎的后果。 CL 作为一种语言,没有顶级 词法 变量的概念。
因此,许多实现所做的是对某些东西的顶层绑定有某种非正式的概念,这并不意味着特殊的声明:如果你只是在顶层说 (setf x 3)
那么这不会传染整个环境。但是随之而来的是各种尴尬的问题:这样做之后,例如(symbol-value 'x)
的结果是什么?
幸运的是 CL 是一种强大的语言,很可能 定义 语言中的顶级词法变量。这是一个非常 hacky 的实现,叫做 deflexical
。请注意,那里有更好的实现(至少包括我的一个,我现在找不到):这并不是一个万无一失的解决方案。
(defmacro deflexical (var &optional value)
;; Define a cheap-and-nasty global lexical variable. In this
;; implementation, global lexicals are not boundp and the global
;; lexical value is not stored in the symbol-value of the symbol.
;;
;; This implementation is *not* properly thought-through and is
;; without question problematic
`(progn
(define-symbol-macro ,var (get ',var 'lexical-value))
(let ((flag (cons nil nil)))
;; assign a value only if there is not one already, like DEFVAR
(when (eq (get ',var 'lexical-value flag) flag)
(setf (get ',var 'lexical-value) ,value))
;; Return the symbol
',var)))