lambda 注意点

(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
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的引用也仅仅存在于这个函数,当函数结束以后也就什么都没有啦。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值