(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 执行一次
![](https://img-my.csdn.net/uploads/201207/19/1342705367_4494.jpg)
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
5
3:调用
*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
99
4:上面的例子
3
又牵涉到一个变量的词法作用域的闭包的概念。
当只有一个let表达式的时候,很明显count会作为一个词法变量可以被setf访问,这个时候就有个问题了。因为lambda对象可以作为一个参数进行传递,假如一个函数接受了这个lambda对象,也就是说lambda已经不再let的范围内了,那么本来的绑定count怎么说呢?这个也就如*fn* 所显示的那样,每一次的执行,都是建立在上一次的值的结果之上。count这个最初在let中的绑定,将会尽可能的保存下来,只要有一个地方保持了对let所返回的函数对象lambda的引用就行。
与上面相对的就是函数fn(), (funcall (fn)),虽然(fn)也返回了一个函数对象lambda,但是对这个lambda的引用也仅仅存在于这个函数,当函数结束以后也就什么都没有啦。