检测全局范围的宏

Macro to detect global scope

据我所知,Common Lisp 没有全局词法作用域,所以如果你想创建一个全局变量,你必须使用 defvar 而不是 setq。我正在尝试创建一个自动执行此操作的宏,即我想写

(= x 1)

并拥有它 "just work" 无论我身在何处。我想宏看起来像

(defmacro = (name value)
  `(,(if (is-global) 'defvar 'setf) ,name ,value))

如何写is-global

defvardefparameter 都使全局特殊变量具有动态范围。 defvar 只会创建尚未定义的变量。因此。如果你有一个计数器并重新加载你的源代码,如果它是用 defvar 定义的,它不会被重置,但如果它是用 defparameter.

定义的,它会重新开始

setf 以符号作为第一个参数实际上是 setq 的宏,它改变了一个变量。它不关心变量是全局变量、词法变量还是特殊变量。

在代码深处定义新的全局变量是一种非常糟糕的做法。你会得到惊喜。特殊变量是动态捕获的,因此您可以获得非常难以调试的行为。这就是为什么我们使用 *earmuffs* 的命名约定来区分词法变量和特殊变量。

无论如何,如果您只想创建一个全局变量并确保将值设置为您想要的值,请使用 defparameter。无论您身在何处,它都能正常工作。

(defparameter *test* 10) ; ==> *test*
*test*                   ; ==> 10

重新定义=是不明智的,因为它是一个用于检查数字相等性的函数。

引入绑定

From what I understand, Common Lisp has no global lexical scope,

这部分是正确的,但有一些 "typical" 解决方法。没有规范的实现,但 searching for deflexical 可以引导您进行一些实现。

so if you want to create a global variable you have to use defvar instead of setq. (emphasis added)

这是不正确的。在大多数情况下,您实际上并没有 创建 变量。您将绑定引入环境。最常见的方法是使用 let 或作为 function 的参数。例如:

(defun foo (bar)
   ;; in here, there's a variable `bar`
   )

(let ((a ...))
  ;; a is bound in here

这些是词法绑定,除非在源代码中标识变量的符号被声明为特殊的,这在 Common Lisp 中意味着它是一个动态作用域的变量。您可以进行特殊声明,例如:

(defun foo (bar)
   (declare (special bar))
   ;; in here, there's a variable `bar`
   )

(let ((a ...))
  (declare (special a))
  ;; a is bound in here

现在,您还可以使用defparameterdefvar引入全局变量。这些 globally 将变量声明为特殊变量。

正在更新绑定

两种情况下,可以使用setqsetf更新值一个变量。也就是说,您可以使用 setqsetf 来更新词法变量和特殊变量的值。所以你可以这样做:

(defparameter *cat* (make-initial-cat))

(let ((cat (some-local-cat)))
  (setf *cat* (make-instance 'cat)) ; update global/dynamic
  (setf cat (make-instance 'cat))   ; update local/lexical

setf 在这两种情况下都有效,所以听起来您正在寻找的赋值运算符只是 setf.

听起来您正在尝试解决的问题是您不应该使用 setf/setq带有未声明的变量。事实上,这是未定义的行为。所以听起来你试图让你的赋值运算符在周围环境中没有变量的情况下自动引入一个变量。您不能这样做,至少有两个原因:

  1. 你怎么知道是引入词法变量还是动态变量?不能从周围环境判断,因为如果周围环境已经有了,就不用介绍了。
  2. 无法检查它是否已声明为局部变量或动态变量。有一些变通方法在某些情况下会起作用,但环境访问不是标准 Common Lisp 的一部分。 (见一个相关问题:. Some implementations do implement the CLtL2 environnments API。)