(lambda (parameter) body)
Lambda表达式可以视为特殊的函数名,名字本身就直接描述函数的用途。这就是为啥可以用带#'的lambda表达式来代替一个函数名。同时他跟单纯的函数名有区别。
比如你直接输入plot,他怎么会知道这个就是个函数名字呢?所以会说plot unbound,你必须得指明它是一个函数对象#'
注意点:下面这个例子也说明了,无论你lambda前有没有#'它的值都是一个lambda对象,即函数对象。
CL-USER> (defun plot(fn min max step) (loop for i from min to max by step do (loop repeat (funcall #'fn i) do(format t "*")) (format t "~%"))) PLOT CL-USER> plot ; No value CL-USER> #'plot #<FUNCTION PLOT> CL-USER> (funcall #'(lambda (x y) (+ x y)) 2 3) 5 CL-USER> (lambda (x y) (+ x Y)) #<FUNCTION (LAMBDA (X Y)) {24F29995}> CL-USER> #'(lambda (x y) (+ x y)) #<FUNCTION (LAMBDA (X Y)) {2525B7D5}>
易错点:
1:函数中定义lambda.
请看下面程序,我原想着当我调用fn函数的时候可以更改*test* 的值,但是最后返回了一个lambda 对象,然后我就试着是不是因为没有输入funcall 的原因,谁知同样是不会改变*test*的值,其实(fn)就是求fn得值,跟(funcall fn)一样,都是会返回一个fn最后表达式的值。也就是lambda对象,我其实是想让这段逻辑执行掉,现在就是不执行。实际上是缺少了在对他进行一次funcall。
并且你会注意到setf 中第二个参数实际上应该在第一次调用(fn)时就执行到了,所以才会在后来无论你再调用多少次都是5的原因。
如果想从根本上解决这个问题就是在函数fn定义的时候就funcall lambda对象,因为lambda的值是一个函数对象,你再funcall 一下,就相当于执行它了,也就是式中最后一个表达式的值了。
总之如果注意到(fn)/(funcall fn)值都是一个lambda对象的话,就应该顺着想到再用funcall 执行一次
2:let 中调用lambda,第一个代码中永远也不会执行lambda,因为你如果在let外层调用一个funcall的话,他会报错,因为本身let的值是最后一个表达式的值,如下两个都是一个数字,没有办法再被funcall了,如果let最后一个表达式是lambda对象的话,见3分析。
CL-USER> (let ((i 3)) (setf i 4) (format t "~s~%" i) (lambda ()(setf i 5)) (format t "~s~%" i)) 4 4 CL-USER> (let ((i 3)) (setf i 4) (format t "~s~%" i) (funcall (lambda ()(setf i 5))) (format t "~s~%" i)) 4 53:调用 *fn* 与 (funcall (fn)) 对比一下,你会发现第一次调用的话,大家所有的代码都执行了,而 *fn* 在第二次执行的时候, format 就不在执行了,而是只是执行 lambda 代码,而 (funcall (fn))与 (fn) 都同样执行了 Lambda 以上的输出逻辑。但是不要忘了, *fn* 是一个全局变量可以存储的,而 (funcall (fn)) 你的执行得到 (fn) 的值,而得到 (fn) 值就是执行全部的过程,除了最后把最后一个表达式的值返回过去,然后接着被 funcall 调用。你或者从另一个角度考虑,因为你是看着这个式子所以你知道 (fn) 的值就是 lambda 对象,然后你就让 funcall 调用这个对象得到一个值就行了,如果这样的话,即使是不同的实参他们的结果也都一样了,因为中间过程你一个都没有执行啊。每一次执行 (funcall (fn)) 他们互不影响,他们彼此之间是独立事件。所以都会从内部开始直到执行到最外层。
CL-USER> (defparameter *fn* (let ((count 0)) (format t "~s2~%" count) #'(lambda ()(setf count (1+ count))))) 02 ---为啥就有一值, *FN* CL-USER> (funcall *fn*) 1 --format 的跑哪去了? CL-USER> (funcall *fn*) 2 CL-USER> (defun fn() (let ((i 3)) (setf i 4) (format t "~s~%" i) (lambda ()(setf i 5)) (format t "~s~%" i) (lambda ()(setf i 6)) (format t "~s~%" i) (lambda ()(setf *test* 99)))) STYLE-WARNING: redefining COMMON-LISP-USER::FN in DEFUN FN CL-USER> (fn) 4 4 4 #<FUNCTION (LAMBDA () :IN FN) {249CE465}> CL-USER> (funcall (fn)) 4 4 4 994:上面的例子 3 又牵涉到一个变量的词法作用域的闭包的概念。
当只有一个let表达式的时候,很明显count会作为一个词法变量可以被setf访问,这个时候就有个问题了。因为lambda对象可以作为一个参数进行传递,假如一个函数接受了这个lambda对象,也就是说lambda已经不再let的范围内了,那么本来的绑定count怎么说呢?这个也就如*fn* 所显示的那样,每一次的执行,都是建立在上一次的值的结果之上。count这个最初在let中的绑定,将会尽可能的保存下来,只要有一个地方保持了对let所返回的函数对象lambda的引用就行。
与上面相对的就是函数fn(), (funcall (fn)),虽然(fn)也返回了一个函数对象lambda,但是对这个lambda的引用也仅仅存在于这个函数,当函数结束以后也就什么都没有啦。