Macros 宏来检测全局范围

Macros 宏来检测全局范围,macros,scope,lisp,common-lisp,Macros,Scope,Lisp,Common Lisp,据我所知,Common Lisp没有全局词法作用域,因此如果要创建全局变量,必须使用defvar而不是setq。我正在尝试创建一个宏,它会自动执行此操作,也就是说,我想编写 (= x 1) 不管我在哪里,都要让它“工作”。我想宏应该是这样的 (defmacro = (name value) `(,(if (is-global) 'defvar 'setf) ,name ,value)) 如何编写是全局的?这两个defvar和defparameter使全局特殊变量具有动态范围defvar仅

据我所知,Common Lisp没有全局词法作用域,因此如果要创建全局变量,必须使用
defvar
而不是
setq
。我正在尝试创建一个宏,它会自动执行此操作,也就是说,我想编写

(= x 1)
不管我在哪里,都要让它“工作”。我想宏应该是这样的

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

如何编写
是全局的

这两个
defvar
defparameter
使全局特殊变量具有动态范围
defvar
仅在尚未定义变量时创建变量。因此如果您有一个计数器并重新加载源代码,那么如果使用
defvar
定义它,它将不会重置,但是如果使用
defparameter
定义它,它将重新启动

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

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

无论如何,如果您只想创建一个全局变量,并确保该值设置为所需的值,请使用
defparameter
。不管你在哪里,它都会起作用

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

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

这两个
defvar
defparameter
都使全局特殊变量具有动态范围
defvar
仅在尚未定义变量时创建变量。因此如果您有一个计数器并重新加载源代码,那么如果使用
defvar
定义它,它将不会重置,但是如果使用
defparameter
定义它,它将重新启动

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

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

无论如何,如果您只想创建一个全局变量,并确保该值设置为所需的值,请使用
defparameter
。不管你在哪里,它都会起作用

(defparameter *test* 10) ; ==> *test*
*test*                   ; ==> 10
重新定义
=
是不明智的,因为它是用于检查数字相等性的函数

引入绑定 据我所知,Common Lisp没有全局词汇范围

这一部分是正确的,但有一些“典型”的变通方法。没有规范的实现,但是可以引导您实现一些实现

因此,如果要创建全局变量,必须使用defvar 而不是setq。(增加重点)

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

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

(let ((a ...))
  ;; a is bound in here
这些都是词法绑定,除非在源代码中标识变量的符号声明为特殊,这在公共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引入全局变量。这些函数全局地将变量声明为特殊变量

更新绑定 在这两种情况下,您都可以使用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。事实上,这是未定义的行为。因此,听起来好像你在试图让赋值运算符在周围环境中没有变量的情况下自动引入变量。您不能这样做,至少有两个原因:

  • 您如何知道是引入词汇变量还是动态变量?您无法从周围环境中确定它,因为如果它已经在周围环境中,您就不需要引入它
  • 无法检查它是否已声明为局部变量或动态变量。有一些变通方法在某些情况下可以使用,但环境访问不是标准公共Lisp的一部分。(请参阅相关问题:。某些实现确实实现了。)
  • 引入绑定 据我所知,Common Lisp没有全局词汇范围

    这一部分是正确的,但有一些“典型”的变通方法。没有规范的实现,但是可以引导您实现一些实现

    因此,如果要创建全局变量,必须使用defvar 而不是setq。(增加重点)

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

    (defun foo (bar)
       ;; in here, there's a variable `bar`
       )
    
    (let ((a ...))
      ;; a is bound in here
    
    这些都是词法绑定,除非在源代码中标识变量的符号声明为特殊,这在公共Lisp中意味着它是一个动态范围的变量。您可以执行以下特殊声明:

    (defun foo (bar)
       (declare (special bar))
       ;; in here, there's a variable `bar`
       )
    
    (let ((a ...))
      (declare (special a))
      ;; a is bound in here
    
    现在,你也可以