在这之上,我们可以创建一个通用的结合函数:
(apply (combiner (car args))
args))
它接受任何类型的参数,并以适合它们类型的方式结合。(为了简化这个例子,我们假定所有的实参,都有着一样的类型。)
> (combine 2 3)
5
> (combine '(a b) '(c d))
(A B C D)
2.10 小节提过词法变量(lexical variables)只在被定义的上下文内有效。伴随这个限制而来的是,只要那个上下文还有在使用,它们就保证会是有效的。
如果函数在词法变量的作用域里被定义时,函数仍可引用到那个变量,即便函数被作为一个值返回了,返回至词法变量被创建的上下文之外。下面我们创建了一个把实参加上 3
的函数:
闭包结合了函数与环境(environment);无论何时,当一个函数引用到周围词法环境的某个东西时,闭包就被隐式地创建出来了。这悄悄地发生在像是下面这个函数,是一样的概念:
(mapcar #'(lambda (x)
lst))
这函数接受一个数字及列表,并返回一个列表,列表元素是元素与传入数字的和。在 lambda 表达式里的变量 num
是自由的,所以像是这样的情况,我们传递了一个闭包给 mapcar
。
一个更显着的例子会是函数在被调用时,每次都返回不同的闭包。下面这个函数返回一个加法器(adder):
(defun make-adder (n)
#'(lambda (x)
(+ x n)))
它接受一个数字,并返回一个将该数字与其参数相加的闭包(函数)。
(let ((counter 0))
(setf counter 0))
(setf counter (+ counter 1))))
这样的一对函数或许可以用来创建时间戳章(time-stamps)。每次我们调用 stamp
时,我们获得一个比之前高的数字,而调用 reset
我们可以将计数器归零:
> (list (stamp) (stamp) (reset) (stamp))
(1 2 0 1)
你可以使用全局计数器来做到同样的事情,但这样子使用计数器,可以保护计数器被非预期的引用。
Common Lisp 有一个内置的函数 complement
函数,接受一个谓词,并返回谓词的补数(complement)。比如:
有了闭包以后,很容易就可以写出这样的函数:
(defun our-complement (f)
#'(lambda (&rest args)