17.6 闭包函数对象

字节编译函数使用一种特殊的数据类型:闭包。 闭包既用于字节编译的 Lisp 函数,也用于解释执行的 Lisp 函数。 当此类对象作为待调用的函数出现时,Emacs 会使用相应的解释器 来执行字节码或未编译的 Lisp 代码。

在内部实现上,闭包与向量非常相似; 可以使用 aref 访问其元素。 它的打印表示形式也与向量类似,但在左方括号 ‘[’ 前多了一个 ‘#’。 闭包至少包含三个元素,无最大数量限制,但仅有前六个元素有常规用途:

argdesc

参数描述符。它可以是参数列表(详见 参数列表的特性), 也可以是对所需参数数量进行编码的整数。 若为整数形式: 第 0~6 位表示参数的最小数量; 第 8~14 位表示参数的最大数量; 若参数列表使用 &rest,则第 7 位设为 1;否则为 0。

当闭包是字节码函数时: 若 argdesc 是列表,执行字节码前会对参数进行动态绑定; 若 argdesc 是整数,执行代码前会将参数压入字节码解释器的栈中。

code

对于解释执行的函数,该元素是构成函数体的(非空)Lisp 形式列表; 对于字节编译的函数,该元素是包含字节码指令的字符串。

constants

对于字节编译的函数,该元素存储字节码引用的 Lisp 对象向量, 包括用作函数名和变量名的符号。 对于解释执行的函数: 若函数使用旧版 Emacs Lisp 的动态作用域方言,该元素为 nil; 否则,该元素存储函数的词法环境。

stacksize

该函数所需的最大栈空间。此元素对解释执行的函数无实际作用。

docstring

文档字符串(若有);否则为 nil。 若文档字符串存储在文件中,该值也可能是数字或列表。 可使用 documentation 函数获取真实的文档字符串(see Access to Documentation Strings)。

interactive

交互规范(若有)。可以是字符串或 Lisp 表达式; 非交互式函数的该元素值为 nil

以下是字节码函数对象的打印表示示例,对应命令 backward-sexp 的定义:

#[256
  "\211\204^G^@\300\262^A\301^A[!\207"
  [1 forward-sexp]
  3
  1793299
  "^p"]

创建字节码对象的原生方法是使用 make-byte-code

Function: make-byte-code &rest elements

该函数构造并返回一个闭包,作为以 elements 为元素的字节码函数对象。

请勿自行构造字节码函数的元素——若元素内容不一致, 调用该函数时可能导致 Emacs 崩溃。 应始终交由字节编译器创建这些对象(编译器会保证元素的一致性)。

创建解释执行函数的原生方法是使用 make-interpreted-closure

Function: make-interpreted-closure args body env &optional docstring iform

该函数构造并返回表示解释执行函数的闭包: args:函数的参数列表; body:函数体(必须是非 nil 的 Lisp 形式列表); env:词法环境(格式与 eval 所用一致,see 求值); docstring(可选):文档字符串(非 nil 时应为字符串); iform(可选):交互形式(非 nil 时应为 (interactive arg-descriptor) 格式,see Using interactive)。


emacs

Emacs

org-mode

Orgmode

Donations

打赏

Copyright

© 2025 Jasper Hsu

Creative Commons

Creative Commons

Attribute

Attribute

Noncommercial

Noncommercial

Share Alike

Share Alike