2.14 函数作为对象 (Functions as Objects)
函数在 Lisp 里,和符号、字符串或列表一样,是稀松平常的对象。如果我们把函数的名字传给 function
,它会返回相关联的对象。和 quote
类似, function
是一个特殊操作符,所以我们无需引用(quote)它的实参:
> (function +)
#<Compiled-Function + 17BA4E>
这看起来很奇怪的返回值,是在典型的 Common Lisp 实现里,函数可能的打印表示法。
到目前为止,我们仅讨论过,不管是 Lisp 打印它们,还是我们输入它们,看起来都是一样的对象。但这个惯例对函数不适用。一个像是 +
的内置函数 ,在内部可能是一段机器语言代码(machine language code)。每个 Common Lisp 实现,可以选择任何它喜欢的外部表示法(external representation)。
如同我们可以用 '
作为 quote
的缩写,也可以用 #'
作为 function
的缩写:
> #'+
#<Compiled-Function + 17BA4E>
这个缩写称之为升引号(sharp-quote)。
和别种对象类似,可以把函数当作实参传入。有个接受函数作为实参的函数是 apply
。apply
接受一个函数和实参列表,并返回把传入函数应用在实参列表的结果:
> (apply #'+ '(1 2 3))
6
> (+ 1 2 3)
6
apply
可以接受任意数量的实参,只要最后一个实参是列表即可:
> (apply #'+ 1 2 '(3 4 5))
15
函数 funcall
做的是一样的事情,但不需要把实参包装成列表。
> (funcall #'+ 1 2 3)
6
Note
什么是 lambda
?
lambda
表达式里的 lambda
不是一个操作符。而只是个符号。 在早期的 Lisp 方言里, lambda
存在的原因是:由于函数在内部是用列表来表示, 因此辨别列表与函数的方法,就是检查第一个元素是否为 lambda
。
在 Common Lisp 里,你可以用列表来表达函数, 函数在内部会被表示成独特的函数对象。因此不再需要 lambda 了。 如果需要把函数记为
((x) (+ x 100))
而不是
(lambda (x) (+ x 100))
也是可以的。
但 Lisp 程序员习惯用符号 lambda
,来撰写函数, 因此 Common Lisp 为了传统,而保留了 lambda
。
defun
宏,创建一个函数并给函数命名。但函数不需要有名字,而且我们不需要 defun
来定义他们。和大多数的 Lisp 对象一样,我们可以直接引用函数。
要直接引用整数,我们使用一系列的数字;要直接引用一个函数,我们使用所谓的lambda 表达式。一个 lambda
表达式是一个列表,列表包含符号 lambda
,接着是形参列表,以及由零个或多个表达式所组成的函数体。
下面的 lambda
表达式,表示一个接受两个数字并返回两者之和的函数:
(lambda (x y)
(+ x y))
列表 (x y)
是形参列表,跟在它后面的是函数主体。
一个 lambda
表达式可以作为函数名。和普通的函数名称一样, lambda 表达式也可以是函数调用的第一个元素,
> ((lambda (x) (+ x 100)) 1)
101
而通过在 lambda
表达式前面贴上 #'
,我们得到对应的函数,
> (funcall #'(lambda (x) (+ x 100))
1)
lambda
表示法除上述用途以外,还允许我们使用匿名函数。