defun
宏在Lisp中,像 mark-whole-buffer
这样的符号附有与其关联的代码,告诉计算机在调用该函数时应该执行什么操作。这段代码称为 函数定义,它是通过评估以符号 defun
(它是 define function 的缩写)开头的Lisp表达式创建的。
在后续的章节中,我们将查看Emacs源代码中的函数定义,比如 mark-whole-buffer
。在本节中,我们将描述一个简单的函数定义,以便您了解其外观。这个函数定义使用算术运算,因为这样做可以提供一个简单的例子。有些人不喜欢使用算术的例子;然而,如果您是这样的人,请不要绝望。在本介绍的其余部分中,我们将几乎不涉及算术或数学的代码。例子主要涉及以某种方式涉及文本的内容。
一个函数定义由跟在 defun
之后的最多五个部分组成:
()
。
有助于将函数定义的五个部分视为一个模板,其中为每个部分都留有槽位:
(defun function-name (arguments…)
"optional-documentation…"
(interactive argument-passing-info) ; 可选
body…)
以一个将其参数乘以7的函数为例说明。 (此示例不是交互式的。有关此信息,请参阅See 使函数具有交互性.)
(defun multiply-by-seven (number) "将NUMBER乘以七。" (* 7 number))
这个定义以括号和符号defun
开始,后跟函数的名称。
函数名称后面是一个包含将传递给函数的参数的列表。 这个列表称为参数列表。 在这个例子中,列表只有一个元素,即符号number
。 当使用函数时,该符号将绑定到作为函数参数的值。
与选择number
作为参数名称不同,我可以选择任何其他名称。 例如,我可以选择单词multiplicand
。 我选择了单词“number”是因为它说明了此槽位所需的值的类型; 但是我完全可以选择单词“multiplicand”来指示在函数运作中放入此槽位的值所起的作用。 我可以称其为foogle
,但那是一个不好的选择,因为它不会告诉人们它的含义。 名称的选择由程序员决定,应该选择使函数的含义清晰的名称。
确实,你可以为参数列表中的符号选择任何你喜欢的名称,甚至是在其他函数中使用的符号的名称:在参数列表中使用的名称对于该特定定义是私有的。在该定义中,该名称指的是与函数定义之外使用的同名实体不同的实体。假设在你的家庭中你有一个绰号“矮子”;当你的家庭成员提到“矮子”时,他们指的是你。但在你的家庭之外,比如在电影中,“矮子”这个名字指的是别人。因为参数列表中的名称对函数定义是私有的,你可以在函数体内改变这样一个符号的值,而不会改变它在函数外的值。这个效果类似于‘let‘表达式(See let
.)。
参数列表后面是描述函数的文档字符串。这是当你键入C-h f和函数的名称时所看到的内容。顺便说一句,当你编写这样的文档字符串时,应该将第一行写成完整的句子,因为有些命令(比如apropos
)只打印多行文档字符串的第一行。此外,如果有第二行文档字符串,不应该对其进行缩进,因为在使用C-h f(describe-function
)时,这样看起来很奇怪。文档字符串是可选的,但它非常有用,几乎应该包含在你编写的每个函数中。
该示例的第三行是函数定义的主体。 (当然,大多数函数的定义要比这个长。)在这个函数中,主体是列表(* 7 number)
,它表示将number的值乘以7。(在Emacs Lisp中,*
是乘法的函数,就像+
是加法的函数一样。)
当你使用 multiply-by-seven
函数时,参数 number
会被评估为你希望使用的实际数字。下面是展示如何使用 multiply-by-seven
的示例;但现在不要尝试评估它!
(multiply-by-seven 3)
在函数定义的下一部分中指定的符号 number
在实际使用函数时被绑定到值 3。请注意,尽管在函数定义中 number
处于括号中,但传递给 multiply-by-seven
函数的参数不在括号中。括号写在函数定义中,这样计算机可以确定参数列表的结束位置和函数定义的其余部分的开始位置。
如果你评估这个示例,很可能会收到错误消息。(试试看吧!)这是因为我们已经写了函数定义,但还没有告诉计算机有关定义——我们还没有在Emacs中加载函数定义。安装函数是告诉Lisp解释器函数定义的过程。安装过程在下一部分中描述。