1

我正在尝试 Emacs 词法作用域(Emacs 24 的新特性)之间add-to-list的相互作用,发现相互作用令人困惑,我不知道如何理解它。这是一个最小的示例,除了我使用set而不是add-to-list. (set类似于add-to-list它通常采用带引号的变量名)

(eval
 '(progn
    (setq a "global")
    (let ((a "apple"))
      (defun my-print-a ()
        (print a)
        (set 'a "by set")
        (print a))
      (setq a "mature apple"))
    (let ((a "banana"))
      (my-print-a))
    (print a))
 t) ;; t for lexical scoping

上面的代码按顺序打印“mature apple”、“mature apple”、“by set”。第一个打印结果“成熟的苹果”与词法作用域的预期一致(支持词法闭包),在这里看到并不奇怪。但是第二次和第三次打印的结果让我感到惊讶。好像(set 'a "by set")只是识别和影响 name 的全局绑定a

这是预期的行为吗?或者这是一个错误?如果有意,如何理解这种行为?

我是否正确地假设set只要词法范围打开,它总是会影响全局绑定?

使用(eval '(progn ...) nil)时,事情会按照动态作用域的预期工作,并且 的行为与这种情况下的行为(set 'a ...)相同(setq a ...)。只有当一个人同时使用词法作用域和带引号的变量时,才会出现这个问题。


更新:

根据手册,这似乎是一种预期的行为。在Void Variables上,手册说

在词法绑定规则下,值单元只保存变量的全局值,即任何词法绑定结构之外的值。当一个变量被词法绑定时,局部值由词法环境决定;如果其符号的值单元格未分配,则该变量可能具有局部值。

词法绑定

symbol-value、boundp 和 set 等函数仅检索或修改变量的动态绑定(即其符号值单元格的内容)。

symbol-value、boundp、set 是通常用带引号的变量名 ( (symbol-value 'var) (boundp 'var) (set 'var 123)) 调用的函数。这些函数仅获取或设置符号的值单元格,并且在词法绑定规则下,值单元格仅保存全局值。所以在词法绑定下,使用带引号的变量只能获取或设置全局值(除非变量是特殊变量)。尽管结果既不是词汇(苹果)也不是动态(香蕉)看起来仍然很奇怪。

4

1 回答 1

1

代码的编写方式与 Emacs 期望的启用词法作用域的程序不同。

http://www.gnu.org/software/emacs/manual/html_node/elisp/Definitions.html

defvar并且defconst是将符号定义为全局变量的特殊形式——可以在 Lisp 程序中的任何位置访问的变量。

(...)

原则上,您可以将变量值分配给任何带有 的符号setq,无论它是否首先被定义为变量。但是,您应该为要使用的每个全局变量编写一个变量定义;否则,如果在启用词法作用域的情况下对其进行评估,您的 Lisp 程序可能无法正确运行。

http://www.gnu.org/software/emacs/manual/html_node/elisp/Lexical-Binding.html

请注意,类似symbol-valueboundp和的函数set仅检索或修改变量的动态绑定(即其符号的值单元格的内容)。此外, a defunor主体中的代码defmacro不能引用周围的词法变量。

http://www.gnu.org/software/emacs/manual/html_node/elisp/Setting-Variables.html

特殊形式:setq[符号 形式]...

这种特殊形式是更改变量值的最常用方法。(...) 符号的当前绑定已更改。

(...)

功能:set 符号

此函数将value放入symbol的 value 单元格中。

(...)

当动态变量绑定生效(默认)时,set与 具有相同的效果,除了计算其符号参数而没有计算setq的事实。但是当一个变量被词法绑定时,会影响它的动态值,而影响它的当前(词法)值。setsetqsetsetq

如果我们将defvar定义添加a为全局变量,我们可以看到函数a中的所有引用my-print-a都被动态绑定,如手册所述

(eval
 '(progn
    (defvar a nil)
    (setq a "global")
    (let ((a "apple"))
      (defun my-print-a ()
        (print a)                       ; "banana"
        (set 'a "by set")              
        (print a))                      ; "by set"
      (setq a "mature apple"))
    (let ((a "banana"))
      (my-print-a))
    (print a))                          ; "global"
 t)
于 2012-08-18T16:54:37.797 回答