13.14 内联函数

内联函数(inline function) 的行为与普通函数完全一致,仅存在一处差异:当你对该函数的调用进行字节编译时(see 字节编译),函数的定义会被直接展开到调用处。

定义内联函数的简便方式,是将定义时的 defun 替换为 defsubst。定义的其余部分保持不变,而使用 defsubst 会指定该函数在字节编译时以内联方式处理。

Macro: defsubst name args [doc] [declare] [interactive] body…

该宏用于定义内联函数。其语法与 defun 完全相同(see 定义函数)。

将函数设为内联通常能提升函数调用的执行速度,但也存在缺点。其一,它会降低灵活性:如果你修改了函数的定义,已被内联的调用仍会使用旧定义,除非重新编译这些调用代码。

另一个缺点是,将大型函数设为内联会增大编译后代码在文件和内存中的体积。由于内联函数的速度优势在小型函数上体现得最为明显,因此通常不应该将大型函数设为内联。

此外,内联函数在调试、跟踪和 advice 增强(see 为 Emacs Lisp 函数添加建议)方面表现不佳。由于便于调试和可灵活重定义函数是 Emacs 的重要特性,因此即使函数很小,你也不应该将其设为内联,除非其执行速度确实至关重要,并且你已经通过计时测试确认使用 defun 确实存在性能问题。

内联函数定义之后,可以在同一个文件的后续位置对其执行内联展开,就像宏一样。

可以使用 defmacro 定义一个宏,使其展开为与内联函数执行时完全相同的代码(see )。但宏只能在表达式中直接使用 —— 宏不能通过 applymapcar 等方式调用。此外,将普通函数转换为宏需要一定的工作量。而将其转换为内联函数则很简单:只需把 defun 替换成 defsubst。由于内联函数的每个参数都只会被精确求值一次,你不必像使用宏那样担心函数体中会多次使用参数。

作为另一种方案,你可以通过提供一段代码来定义函数,让字节编译器将其作为编译器宏进行内联展开(see declare 形式)。下面这些宏可以实现这一点。

Macro: define-inline name args [doc] [declare] body…

该宏通过提供一段实现内联展开的代码(作为编译器宏)来定义函数 name。该函数将接受参数列表 args,并执行指定的函数体 body

若提供了 doc,则其应为该函数的文档字符串(see 函数的文档字符串);若提供了 declare,则其应为一个 declare 形式的声明(see declare 形式),用于指定函数的元数据。

通过 define-inline 定义的函数,相比通过 defsubstdefmacro 定义的宏,具有以下多个优势:

defmacro 类似,使用 define-inline 定义的内联函数,其作用域规则(动态作用域或词法作用域)继承自调用位置。See Scoping 变量绑定的作用域规则

下面这些宏应当在由 define-inline 定义的函数体中使用。

Macro: inline-quote expression

define-inline 引用 expression。其作用类似于反引号(see 反引号),但只用于引用代码,且只接受 ,,不接受 ,@

Macro: inline-letevals (bindings…) body…

该宏提供了一种便捷方式,用于确保内联函数的参数只被精确求值一次,同时也用于创建局部变量。

它与 let 类似(see 局部变量):按照 bindings 的指定创建局部变量,然后在这些绑定生效的情况下执行 body

bindings 中的每个元素可以是一个符号,或是形如 (var expr) 的列表;效果是对 expr 求值,并将 var 绑定到该结果。但当 bindings 中的元素只是一个符号 var 时,会将对 var 求值后的结果重新绑定给 var 自身(这与 let 的行为截然不同)。

bindings 的末尾部分可以是 nil,也可以是一个用于存放参数列表的符号;若是后者,则会对每个参数求值,并将该符号绑定到求值后的参数列表。

Macro: inline-const-p expression

expression 的值已是已知状态,则返回非 nil 值。

Macro: inline-const-val expression

返回 expression 的值。

Macro: inline-error format &rest args

触发错误,并根据 format 格式化 args 参数列表。

以下是使用 define-inline 的示例:

(define-inline myaccessor (obj)
  (inline-letevals (obj)
    (inline-quote (if (foo-p ,obj) (aref (cdr ,obj) 3) (aref ,obj 2)))))

这段代码等价于

(defsubst myaccessor (obj)
  (if (foo-p obj) (aref (cdr obj) 3) (aref obj 2)))

emacs

Emacs

org-mode

Orgmode

Donations

打赏

Copyright

© 2025 Jasper Hsu

Creative Commons

Creative Commons

Attribute

Attribute

Noncommercial

Noncommercial

Share Alike

Share Alike