joh*_*ers 1 lisp closures common-lisp
考虑这两个:
(defparameter *lfn*
(let ((count 0))
#'(lambda ()
(incf count))))
(defun testclosure ()
(let ((count 0))
#'(lambda ()
(incf count))))
Run Code Online (Sandbox Code Playgroud)
为什么他们的行为不同:
CL-USER> (funcall (testclosure))
1
CL-USER> (funcall (testclosure))
1
CL-USER> (funcall *lfn*)
1
CL-USER> (funcall *lfn*)
2
Run Code Online (Sandbox Code Playgroud)
count在defparameter版本中关闭但不在版本中关闭defun.为什么是这样?
当你*lfn*在一个闭包中创建一个函数时.调用它将增加关闭的计数并对其进行求值.
testclosure与*lfm*每次调用时所做的相同.从而:
(defparameter *lfn2* (testclosure))
(funcall *lfn2*) ; ==> 1
(funcall *lfn2*) ; ==> 2
(funcall *lfn2*) ; ==> 3
Run Code Online (Sandbox Code Playgroud)
将完全相同*lfn*,连续调用它将增加返回的值.然而
(funcall (testclosure)) ; ==> 1 (and the closure can be recycled)
(funcall (testclosure)) ; ==> 1 (and the closure can be recycled)
Run Code Online (Sandbox Code Playgroud)
在这里你正在做funcall一个新创建的闭包,你没有为连续的调用存储,所以它会返回1.然后你funcall再做一个你也没有存储的全新闭包,它的第一个调用也评估为1.
所以答案是,计数在两者中都被关闭,但在你的例子中,你创建了一个新的闭包,并且只使用了一次,多次.
Sylwester的答案很好地解释了这一点,但是在一个具有更明确副作用的例子中,这一点更清楚,请考虑:
CL-USER> (defparameter *foo* (progn (print 'hello) 0))
HELLO
*FOO*
CL-USER> *foo*
0
CL-USER> *foo*
0
Run Code Online (Sandbox Code Playgroud)
在定义中*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
Run Code Online (Sandbox Code Playgroud)
每次foo被调用,(progn (print 'hello) 0)被评估,因此hello被打印并被0返回.看到这个例子后,你的代码应该更清晰一些.
(defparameter *lfn*
(let ((count 0))
#'(lambda ()
(incf count))))
Run Code Online (Sandbox Code Playgroud)
(let ...)被评估一次,并且该评估产生其值的闭包*lfn*.另一方面,在
(defun testclosure ()
(let ((count 0))
#'(lambda ()
(incf count))))
Run Code Online (Sandbox Code Playgroud)
(let ...)每次testclosure调用时都会计算,每次都会返回一个新的闭包.