12.10.2 动态绑定

在现代 Lisp 方言中,特殊变量(见下文)的局部变量绑定是动态的;而在旧版 Lisp 方言中,所有变量的绑定都是动态的。(See 选择 Lisp 方言。) 动态变量绑定有其适用场景,但总体来说比词法绑定更容易出错、效率更低,并且编译器也更难发现使用动态绑定的代码中的错误。

当一个变量是动态绑定时,在 Lisp 程序执行的任意时刻,它的当前绑定就是该符号最近创建的动态局部绑定;如果不存在这样的局部绑定,则使用全局绑定。

动态绑定具有动态作用域和动态存在期,如下例所示:

(defvar x -99)  ; x receives an initial value of −99.

(defun getx ()
  x)            ; x is used free in this function.

(let ((x 1))    ; x is dynamically bound.
  (getx))
     ⇒ 1

;; After the let form finishes, x reverts to its
;; previous value, which is −99.

(getx)
     ⇒ -99

函数 getx 引用了 x。这是一种自由引用(free reference)—— 也就是说,在 defun 这个绑定结构内部,并没有为 x 建立任何绑定。当我们在一个将 x(以动态方式)绑定的 let 表达式内部调用 getx 时,它会获取到这个局部值(即 1);但当我们在 let 表达式外部调用 getx 时,它会获取到全局值(即 −99)。

再看另一个示例,该示例展示了如何使用 setq 设置动态绑定的变量:

(defvar x -99)      ; x receives an initial value of −99.

(defun addx ()
  (setq x (1+ x)))  ; Add 1 to x and return its new value.

(let ((x 1))
  (addx)
  (addx))
     ⇒ 3           ; The two addx calls add to x twice.

;; After the let form finishes, x reverts to its
;; previous value, which is −99.

(addx)
     ⇒ -98

即使启用了词法绑定,某些变量仍然会使用动态绑定。这些变量被称为 特殊变量(special variables)。所有通过 defvardefcustomdefconst 定义的变量都是特殊变量(see 定义全局变量)。其余所有变量则采用词法绑定。

使用不带初始值的 defvar,可以只在单个文件内,或文件的某一部分代码中将变量设为动态绑定,而在其他地方仍然使用词法绑定。例如:

(let (_)
  (defvar x)      ; Let-bindings of x will be dynamic within this let.
  (let ((x -99))  ; This is a dynamic binding of x.
    (defun get-dynamic-x ()
      x)))

(let ((x 'lexical)) ; This is a lexical binding of x.
  (defun get-lexical-x ()
    x))

(let (_)
  (defvar x)
  (let ((x 'dynamic))
    (list (get-lexical-x)
          (get-dynamic-x))))
    ⇒ (lexical dynamic)
Function: special-variable-p symbol

该函数会检查 symbol 是否为特殊变量:如果是(即该变量通过 defvardefcustomdefconst 完成了变量定义),则返回非 nil 值;否则返回 nil

需要注意的是,由于这是一个函数,它仅会对永久特殊的变量返回非 nil 值,而对于仅在当前词法作用域内特殊的变量,不会返回非 nil 值。

不支持将特殊变量用作函数的形式参数。

Emacs Lisp 以一种简单的方式实现动态绑定:每个符号都有一个值单元(value cell),用于存储该符号当前的动态值(或标记无值状态)(See 符号的组成)。当为一个符号创建动态局部绑定时,Emacs 会先将值单元中的原有内容(或无值状态)记录到栈中,再把新的局部值存入值单元;当绑定结构执行完毕后,Emacs 会将栈中保存的旧值弹出,并重新写入值单元。


emacs

Emacs

org-mode

Orgmode

Donations

打赏

Copyright

© 2025 Jasper Hsu

Creative Commons

Creative Commons

Attribute

Attribute

Noncommercial

Noncommercial

Share Alike

Share Alike