【问题标题】:Macro to detect global scope检测全局范围的宏
【发布时间】:2015-04-05 18:31:01
【问题描述】:

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

(= x 1)

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

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

我怎么写is-global

【问题讨论】:

  • SETF 不创建变量,甚至不创建局部变量。
  • “作为一名程序员,我对必须记住这一点感到非常愤慨。”这是关于变量声明的。这类似于说您根本不想编写变量声明/绑定,并且接受一个值数组作为函数的参数(例如,(defun frob-the-bar (&rest args) ...))。我宁愿(defun frob-the-bar (frobber bar &key frobulator) ...)know 论点。在 Common Lisp 中,全局变量几乎总是被“耳罩”(即周围的星号,例如 *standard-output*)包围,因此您确实通常知道哪种类型您拥有的变量。
  • (= x 1) 确实“正常工作”。它返回 x 是否为 1。

标签: macros scope lisp common-lisp


【解决方案1】:

介绍绑定

据我了解,Common Lisp 没有全局词法范围,

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

所以如果你想创建一个全局变量,你必须使用 defvar 而不是 setq。(添加了重点)

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

(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 引入全局变量。这些全局将变量声明为特殊的。

更新绑定

两种情况中,您可以使用 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 的一部分。 (请参阅相关问题:Macro to detect global scope。某些实现确实实现了CLtL2 environnments API。)

【讨论】:

    【解决方案2】:

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

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

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

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-09
      • 2011-03-17
      • 2014-02-22
      • 1970-01-01
      • 2019-08-08
      • 2012-04-29
      • 1970-01-01
      相关资源
      最近更新 更多