用于检测全局范围的宏

时间:2015-04-05 18:31:01

标签: macros scope lisp common-lisp

根据我的理解,Common Lisp没有全局词法范围,因此如果要创建全局变量,则必须使用defvar而不是setq。我正在尝试创建一个自动执行此操作的宏,即我想写

(= x 1)

无论我在哪里,都能“正常工作”。我想这个宏看起来像

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

如何撰写is-global

2 个答案:

答案 0 :(得分:4)

介绍Bindings

  

根据我的理解,Common Lisp没有全局词汇范围,

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

  

因此,如果您想创建全局变量,则必须使用defvar   而不是setq。(强调添加)

这是不正确的。在大多数情况下,您并非真正创建变量。您将绑定引入环境。最常见的方法是使用 let 或作为函数的参数。 E.g:

(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

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

更新绑定

两个案例中,您可以使用 setq setf 来更新变量的值。也就是说,您可以使用 setq setf 来更新词法变量的值以及特殊变量。所以你可以这样做:

(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的一部分。 (参见相关问题:Macro to detect global scope。某些实现确实实现了CLtL2 environnments API。)

答案 1 :(得分:3)

defvardefparameter都会生成具有动态范围的全局特殊变量。 defvar只会在尚未定义的情况下创建变量。从而。如果你有一个计数器并重新加载你的源代码,如果它是用defvar定义的,它将不会被重置,但如果用defparameter定义它将会重新开始。

带有符号作为第一个参数的

setf实际上是setq的宏,它会改变变量。它不关心变量是全局的,词汇的还是特殊的。

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

无论如何,如果您只想创建一个全局变量并确保将值设置为所需的值,请使用defparameter。无论你身在何处,它都会有效。

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

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