这确实是您要增加x 的方式,或者至少是这样做的一种方式。但是,这不是您要绑定x 的方式。在 CL 中,您需要在使用名称之前为它建立一个绑定,而不是仅仅通过分配给它来做到这一点。因此,例如,这段代码(在新的 CL 图像中)是不合法的 CL:
(defun bad ()
(setf y 2))
通常这会导致编译时警告和运行时错误,尽管它可能会做其他事情:它的行为未定义。
您所做的实际上比这更糟糕:您将一个值撞到了x 的symbol-value 中(使用set,它执行此操作),然后假设类似@987654327 @ 将起作用,这是极不可能的。例如考虑这样的事情:
(defun worse ()
(let ((x 2))
(set 'x 4)
(incf x)
(values x (symbol-value 'x))))
这是(与bad 不同)合法代码,但它可能不会按照您的意愿行事。
许多 CL 实现 确实 允许在顶层分配以前未绑定的变量,因为在会话环境中它很方便。但此类赋值的确切含义超出了语言标准。
CMUCL 及其衍生产品(包括 SBCL)在历史上一直比当时的其他实现更加认真。我认为造成这种情况的原因是解释器比大多数其他人更严肃,并且/或者他们无论如何都秘密编译了所有内容,而编译器捡起了东西。
另一个问题是 CL 对顶级变量的语义稍显尴尬:如果您努力以正常方式建立顶级绑定,使用 defvar 和朋友,那么您也会导致变量special -- 动态范围 -- 这是一种普遍的效果:它使 all 该名称的绑定变得特殊。这通常是一个非常不受欢迎的结果。 CL 作为一门语言,没有顶级词法变量的概念。
因此,许多实现所做的就是对不暗示特殊声明的事物进行顶层绑定的某种非正式概念:如果您只是在顶层说(setf x 3),那么这不会传染整个环境。但随之而来的是各种尴尬的问题:这样做之后,比如(symbol-value 'x)的结果是什么?
幸运的是,CL 是一门强大的语言,在语言中定义顶级词法变量是完全可能的。这是一个名为deflexical 的非常hacky 的实现。请注意,那里有更好的实现(包括我的至少一个,我现在找不到):这并不是一个防弹的解决方案。
(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)))