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

通常这会导致编译时警告和 运行 时错误,尽管它可能会执行其他操作:其行为未定义。

你所做的,特别是,实际上比这更糟糕:你已经将一个值添加到 xsymbol-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)))