12.10.1 词法绑定

词法绑定仅在现代版 Emacs Lisp 方言中可用。(See 选择 Lisp 方言.) 使用词法绑定的变量具有 词法作用域(lexical scope),意思是:对该变量的任何引用,在文本上都必须位于该绑定结构内部。下面是一个示例:

(let ((x 1))    ; x is lexically bound.
  (+ x 3))
     ⇒ 4

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

(let ((x 1))    ; x is lexically bound.
  (getx))
error→ Symbol's value as variable is void: x

在此示例中,变量 x 没有全局值。当它在 let 表达式内被词法绑定时,仅能在该 let 表达式的文本范围内使用。但从 let 表达式中调用 getx 函数时,却 无法 在该函数内部使用这个变量 — 因为 getx 的函数定义出现在 let 表达式本身的外部。

词法绑定的工作机制如下:每个绑定结构都会定义一个 词法环境(lexical environment),该环境会指定在这个结构内被绑定的变量及其局部值。当 Lisp 求值器需要获取变量的当前值时,会首先在词法环境中查找;如果变量未在词法环境中定义,则会去符号的值单元(value cell)中查找(动态值即存储于此)。

词法绑定具有无限存在期。即便绑定结构执行完毕,其词法环境仍可通过名为 闭包(closures) 的 Lisp 对象 “留存(kept around)” 下来。当你在启用词法绑定的情况下定义命名函数或匿名函数时,就会创建闭包。See 闭包

当闭包作为函数被调用时,其定义内的所有词法变量引用都会使用留存的词法环境。以下是一个示例:

(defvar my-ticker nil)   ; We will use this dynamically bound
                         ; variable to store a closure.

(let ((x 0))             ; x is lexically bound.
  (setq my-ticker (lambda ()
                    (setq x (1+ x)))))
    ⇒ #f(lambda () [(x 0)]
          (setq x (1+ x)))

(funcall my-ticker)
    ⇒ 1

(funcall my-ticker)
    ⇒ 2

(funcall my-ticker)
    ⇒ 3

x                        ; Note that x has no global value.
error→ Symbol's value as variable is void: x

在这里,let 绑定定义了一个词法环境,其中变量 x 被局部绑定为 0。在这个绑定结构内部,我们定义了一个 lambda 表达式,它将 x 加 1 并返回递增后的值。这个 lambda 表达式会自动变成闭包,其中的词法环境即使在 let 绑定结构退出后依然会继续存在。每次我们对这个闭包求值时,它都会使用那个词法环境里的 x 绑定,并对 x 进行加 1。

注意:与动态变量直接绑定到符号对象本身不同,词法变量与符号之间的关系只存在于解释器(或编译器)内部。因此,接收符号作为参数的函数(如 symbol-valueboundpset)只能获取或修改变量的动态绑定(即符号的值单元里的内容)。

还要注意:变量可以被声明为 special,此时即使是新的绑定(例如 let 绑定)也会使用动态绑定。根据变量声明方式的不同,它可以是全局特殊、单个文件内特殊,或文件中某一段代码内特殊。See 动态绑定


emacs

Emacs

org-mode

Orgmode

Donations

打赏

Copyright

© 2025 Jasper Hsu

Creative Commons

Creative Commons

Attribute

Attribute

Noncommercial

Noncommercial

Share Alike

Share Alike