13.4 定义函数

我们通常在创建函数时为其命名。这一过程称为 定义函数(defining a function),一般通过 defun 宏来完成。本节还会介绍其他定义函数的方式。

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

defun 是定义新 Lisp 函数的常用方式。它将符号 name 定义为一个函数,参数列表为 args(see 参数列表的特性),函数体为 body 中的表达式。nameargs 都不需要加引号。

doc(若存在)应为字符串,用作函数的文档字符串(see 函数的文档字符串)。declare(若存在)应为 declare 形式,用于指定函数元数据(see declare 形式)。interactive(若存在)应为 interactive 形式,用于指定函数的交互式调用方式(see Interactive Call)。

defun 的返回值未定义。

以下是一些示例:

(defun foo () 5)
(foo)
     ⇒ 5

(defun bar (a &optional b &rest c)
    (list a b c))
(bar 1 2 3 4 5)
     ⇒ (1 2 (3 4 5))
(bar 1)
     ⇒ (1 nil nil)
(bar)
error→ Wrong number of arguments.

(defun capitalize-backwards ()
  "Upcase the last letter of the word at point."
  (interactive)
  (backward-word 1)
  (forward-word 1)
  (backward-char 1)
  (capitalize-word 1))

大多数 Emacs 函数都是 Lisp 程序源代码的一部分,在 Emacs Lisp 读取器执行程序之前读取源码时就已定义。不过,你也可以在运行时动态定义函数,例如:在程序代码执行时生成 defun 调用。 如果这样做,需要注意:Emacs 的帮助命令(如 C-h f)会在 *Help* 缓冲区中提供一个跳转到函数定义的按钮,但这类命令可能无法找到动态生成函数的源代码。因为动态生成函数的写法,与通常静态调用 defun 的形式差异很大。 你可以使用 definition-name 属性,让帮助系统更容易找到生成这类函数的代码,详见 see 标准符号属性

请注意不要无意地重定义已存在的函数。defun 甚至会毫无提示、毫无顾忌地重定义 car 这类原语函数。Emacs 不会阻止你这样做,因为有时候重定义是故意为之,而系统无法区分有意与无意的重定义。

Function: defalias name definition &optional doc

该函数将符号 name 定义为一个函数,其函数定义为 definitiondefinition 可以是任意合法的 Lisp 函数、宏、特殊形式(see 特殊形式)、键映射(see Keymaps),也可以是向量或字符串(表示键盘宏)。defalias 的返回值未定义。

doc 不为 nil,则其会成为 name 对应的函数文档。否则,将使用 definition 自身携带的任何文档(如果有)。

在内部实现上,defalias 通常使用 fset 来设置函数定义。但如果 name 拥有 defalias-fset-function 属性,则会调用该属性关联的值(一个函数),以替代 fset 完成设置。

defalias 的适用场景是:定义特定的函数 / 宏名称时 ——尤其是在被加载的源码文件中显式出现该名称的场景。这是因为 defalias 会记录函数的定义文件(与 defun 行为一致,see 卸载)。

与之相反,在以其他目的操作函数定义的程序中,更适合使用 fset(该函数不会保留这类文件记录)。详见 see 访问函数单元内容

如果最终形成的函数定义链出现循环,那么Emacs 会抛出一个 cyclic-function-indirection 错误。

Function: function-alias-p object

检查 object 是否为函数别名。若是,则返回一个由符号组成的列表,代表该函数别名链;否则返回 nil。例如,若 ab 的别名,且 bc 的别名:

(function-alias-p 'a)
    ⇒ (b c)

该函数还有第二个可选参数,但此参数已废弃(obsolete)且无任何实际作用。

你无法使用 defundefalias 创建新的原语函数(primitive function),但可以通过它们修改任意符号的函数定义 —— 即便是 carx-popup-menu 这类默认定义为原语的符号也不例外。不过这种操作存在风险:例如,重新定义 car 几乎必然会导致整个 Lisp 语言彻底无法正常工作。重新定义 x-popup-menu 这类不常用的函数风险相对较低,但仍可能无法达到你预期的效果。如果有 C 代码直接调用该原语函数,这些调用会直接执行原语的 C 语言定义,因此修改符号的函数定义不会对其产生任何影响。

另请参见 defsubst,它与 defun 类似,用于定义函数,同时会告知 Lisp 编译器对该函数执行内联展开。See 内联函数

若要取消定义某个函数名,可使用 fmakunbound。See 访问函数单元内容


emacs

Emacs

org-mode

Orgmode

Donations

打赏

Copyright

© 2025 Jasper Hsu

Creative Commons

Creative Commons

Attribute

Attribute

Noncommercial

Noncommercial

Share Alike

Share Alike