考虑这两个:
(defparameter *lfn*
(let ((count 0))
#'(lambda ()
(incf count))))
(defun testclosure ()
(let ((count 0))
#'(lambda ()
(incf count))))
为什么他们的行为不同:
CL-USER> (funcall (testclosure))
1
CL-USER> (funcall (testclosure))
1
CL-USER> (funcall *lfn*)
1
CL-USER> (funcall *lfn*)
2
count
已在defparameter
版本中关闭,但未在defun
版本中关闭。这是为什么?
答案 0 :(得分:8)
当您创建*lfn*
时,您正在一个闭包内创建一个函数。调用它将增加结束计数并评估它。
testclosure
与每次调用*lfm*
时的操作相同。因此:
(defparameter *lfn2* (testclosure))
(funcall *lfn2*) ; ==> 1
(funcall *lfn2*) ; ==> 2
(funcall *lfn2*) ; ==> 3
将与*lfn*
完全相同,以便连续调用它将增加返回的值。然而
(funcall (testclosure)) ; ==> 1 (and the closure can be recycled)
(funcall (testclosure)) ; ==> 1 (and the closure can be recycled)
这里你正在对新创建的闭包进行funcall
,你没有为连续的调用存储,所以它将返回1.然后你再次对一个全新的闭包进行funcall
你也不存储,它的第一个调用也评估为1.
所以答案是,计数在两者中都被关闭,但在你的例子中,你创建了一个新的闭包并且只使用了几次。
答案 1 :(得分:5)
Sylwester's answer非常好地解释了这一点,但是在一个具有更明确副作用的例子中使这更清楚,请考虑:
CL-USER> (defparameter *foo* (progn (print 'hello) 0))
HELLO
*FOO*
CL-USER> *foo*
0
CL-USER> *foo*
0
在定义*foo*
时,(progn (print 'hello) 0)
会被评估一次,因此会打印hello
,值为0
,这将成为*foo*
的值。稍后评估*foo*
只意味着查找*foo*
的值(0
), not reëvaluating the form that produced its original value. In contrast, consider calling a function whose body is
(progn(print'hello)0)`:
CL-USER> (defun foo () (progn (print 'hello) 0))
FOO
CL-USER> (foo)
HELLO
0
CL-USER> (foo)
HELLO
0
CL-USER> (foo)
HELLO
0
每次调用foo
时,都会评估(progn (print 'hello) 0)
,因此会打印hello
并返回0
。看到这个例子后,你的代码应该更清晰了。
(defparameter *lfn*
(let ((count 0))
#'(lambda ()
(incf count))))
(let ...)
被评估一次,并且该评估产生的值为*lfn*
的闭包。另一方面,在
(defun testclosure ()
(let ((count 0))
#'(lambda ()
(incf count))))
每次调用(let ...)
时都会计算 testclosure
,每次都会返回一个新的闭包。
答案 2 :(得分:2)
*lfn*
的值是一个闭包。
每次调用函数testclosure
都会返回一个新的闭包。