Lambda 表达式可以在形参列表之后可选地包含一段 文档字符串(documentation string)。这段字符串不会影响函数的执行;它类似一种注释,但属于结构化注释,真实存在于 Lisp 环境中,并可被 Emacs 的帮助系统使用。See Documentation 章节介绍了如何访问文档字符串。
为程序中的所有函数提供文档字符串是一个好习惯,即便是那些只在程序内部被调用的函数也应如此。文档字符串与普通注释类似,区别在于它们更容易被访问。
文档字符串的第一行应当独立成意,因为 apropos 只会显示这一行。它应该由一到两句完整的句子组成,概括函数的用途。
文档字符串在源文件中通常会有缩进,但由于这些空格位于起始双引号之前,它们不属于字符串本身。有些人会习惯将字符串里后续的行也做缩进,让源码里看起来对齐。这是错误的做法。后续行的缩进会被包含在字符串内部;在源码里看起来整齐的格式,在帮助命令显示时会变得杂乱难看。
文档字符串后面必须至少跟随一个 Lisp 表达式;否则,它根本不算文档字符串,而会被当作函数体里的单个表达式,并作为返回值使用。
当函数没有有意义的值需要返回时,标准做法是在文档字符串后添加 nil 并将其返回。
文档字符串的最后一行可以用来指定与实际函数参数不同的调用规范。可以像这样书写文本:
\(fn arglist)
在一个空行之后、行首位置书写该行,并且在文档字符串内部该行之后不能换行。(其中的 ‘\’ 用于避免干扰 Emacs 的移动命令。)以这种方式指定的调用规范会出现在帮助信息中,替代从函数实际参数推导出来的调用格式。
该特性对宏定义特别有用,因为宏定义中书写的参数往往不符合用户对宏调用各部分的直观理解。
如果你希望弃用旧的调用规范,并推荐使用上述方式声明的规范,请不要使用该特性。
请改用 advertised-calling-convention 声明(see declare 形式)或 set-advertised-calling-convention(see 声明函数为废弃状态)。因为当字节编译器编译使用了被弃用调用规范的 Lisp 程序时,这两种方式会使其生成警告信息。
(fn) 特性通常用于以下场景:
(defmacro lambda (&rest cdr) "... \(fn ARGS [DOCSTRING] [INTERACTIVE] BODY)"...)
(defmacro macroexp--accumulate (var+list &rest body) "... \(fn (VAR LIST) BODY...)" (declare (indent 1)) (let ((var (car var+list)) (list (cadr var+list)) ...)))
defalias 的用途。示例:
(defalias 'abbrev-get 'get "... \(fn ABBREV PROP)")
文档字符串通常是静态的,但有时需要动态生成。在某些情况下,你可以编写一个宏,在编译时生成包含所需文档字符串的函数代码。不过,你也可以用 (:documentation form) 代替文档字符串,来动态生成它。这会在函数定义时运行时求值 form,并将其作为文档字符串 13。你还可以在文档字符串被查询时即时计算它:只需将函数符号的function-documentation 属性设置为一个求值结果为字符串的 Lisp 表达式。
示例:
(defun adder (x)
(lambda (y)
(:documentation (format "Add %S to the argument Y." x))
(+ x y)))
(defalias 'adder5 (adder 5))
(documentation 'adder5)
⇒ "Add 5 to the argument Y."
(put 'adder5 'function-documentation
'(concat (documentation (symbol-function 'adder5) 'raw)
" Consulted at " (format-time-string "%H:%M:%S")))
(documentation 'adder5)
⇒ "Add 5 to the argument Y. Consulted at 15:52:13"
(documentation 'adder5)
⇒ "Add 5 to the argument Y. Consulted at 15:52:18"