从一般意义上讲,函数是一种根据被称为 参数(arguments) 的输入值来执行计算的规则。计算的结果称为该函数的 值(value) 或 返回值(return value)。计算过程也可能产生副作用,例如永久更改变量的值或数据结构的内容(see Definition of side effect)。纯函数(pure function) 是指这样一类函数:它不仅没有副作用,而且对于相同的参数组合,无论外部因素(如机器类型、系统状态)如何,始终返回相同的值。
在大多数编程语言中,每个函数都有名称。但在 Lisp 中,严格意义上的函数本身是没有名字的:它是一个对象,可以可选地与一个符号(如 car)关联,由该符号充当函数名。See 函数命名。当一个函数被赋予名称后,我们通常也将该符号直接称作 “函数(function)” (例如我们说 “car 函数”)。在本手册中,函数名与函数对象本身之间的区别通常并不重要,但在必要时我们会明确区分。
某些类似函数的对象,称为 特殊形式(special forms) 和 宏(macros), 它们也接受参数来执行计算。 但正如后文所述,在 Emacs Lisp 中它们不被视为函数。
以下是函数及类函数对象的重要术语:
用 Lisp 语言编写的函数(严格意义上的函数,即函数对象)。 下一节将会介绍这些内容。 See Lambda 表达式.
可以从 Lisp 中调用,但实际是用 C 语言编写的函数。原语也被称为 内置函数(built-in functions) 或 subr(子例程)。例如 car、append 这类函数都属于原语。此外,所有特殊形式(见下文)也都被视为原语。
一个函数之所以被实现为原语,通常是因为它是 Lisp 的核心基础(如 car),或是需要为操作系统服务提供底层接口,又或是需要具备很高的运行效率。与用 Lisp 定义的函数不同,原语只能通过修改 C 语言源码并重新编译 Emacs 来修改或添加。参见 Writing Emacs Primitives。
一种类似函数的原语,但不会按常规方式对所有参数求值。它可能只对部分参数求值,或以非常规顺序求值,或是对参数多次求值。例如 if、and、while。See 特殊形式。
一种在 Lisp 中定义的语法结构,它与函数的区别在于:宏会将一个 Lisp 表达式转换为另一个表达式,然后对新表达式求值,而非对原表达式直接求值。宏让 Lisp 程序员能够实现特殊形式才能做到的各类功能。See 宏。
可以通过 command-execute 原语调用的对象,通常是因为用户键入了 绑定(bound) 到该命令的按键序列。See Interactive Call。命令通常是函数;如果该函数是用 Lisp 编写的,可在函数定义中通过 interactive 形式将其变为命令 (see Defining Commands)。作为函数的命令同样可以在 Lisp 表达式中调用,与普通函数无异。
键盘宏(字符串和向量)虽然不是函数,也属于命令。See Keyboard Macros。如果一个符号的函数单元中存放着命令,我们就称该符号为命令(see 符号的组成)。这类 命名命令(named command) 可以通过 M-x 调用。
一种与 lambda 表达式十分相似的函数对象,区别在于它还封装了词法变量绑定的环境。 See 闭包。
经过字节编译器编译后的函数。 See 闭包函数类型。
作为真实函数的占位符。当调用该自动加载对象时, Emacs 会先加载包含真实函数定义的文件,随后调用这个真实函数。 See 自动加载。
你可以使用 functionp 函数来检测一个对象是否为函数:
若 object(对象)是任意类型的函数(即可以传递给 funcall 函数调用),
该函数返回 t。需注意:对于作为函数名的符号,functionp 返回 t;
对于作为宏或特殊形式的符号,functionp 返回 nil。
若 object 不是函数,该函数通常返回 nil。
但函数对象的表示形式较为复杂,出于效率考量,在极少数情况下,
即便 object 并非函数,该函数也可能返回 t。
你也可以获取任意函数预期接收的参数数量:
该函数提供指定 function(函数)的参数列表相关信息。
返回值为一个点对(cons cell),格式为 (min . max):
其中 min 是参数的最小数量;
max 则为参数的最大数量——若函数包含 &rest 参数,max 为符号 many;
若 function 是特殊形式,max 为符号 unevalled。
需注意,该函数在部分场景下可能返回不准确的结果,例如以下情况:
apply-partially 定义的函数(see apply-partially)。
advice-add 为其添加了建议的函数(see 为命名函数添加建议)。
与 functionp 不同,后续这些函数 不会 将符号视为其对应的函数定义。
若 object(对象)是内置函数(即 Lisp 原语),该函数返回 t。
(subrp 'message) ; message is a symbol,
⇒ nil ; not a subr object.
(subrp (symbol-function 'message))
⇒ t
若 object(对象)是字节码函数,该函数返回 t。示例如下:
(byte-code-function-p (symbol-function 'next-line))
⇒ t
若 object 是非 ELisp 源码形式的函数对象(而是类似机器码或字节码的形式),
该函数返回 t。更具体地说,当函数满足以下任一条件时,该函数返回 t:
内置函数(也称为 “原语(primitive)” ,see 什么是函数?)、
字节编译函数(see 字节编译)、
原生编译函数(see Lisp 本地代码编译),
或从动态模块加载的函数(see Emacs 动态模块)。
若 object(对象)是解释型函数,该函数返回 t。
若 object 是闭包(一种特定类型的函数对象),该函数返回 t。
目前,所有字节码函数和所有解释型函数均以闭包形式实现。
该函数的功能与 func-arity 类似,但仅适用于内置函数,
且不会进行符号间接解析。若传入非内置函数,该函数会触发错误。
我们建议优先使用 func-arity 而非此函数。
该函数与 functionp 功能类似,区别在于:
对于列表和符号类型的对象,该函数会返回 nil。