为什么这个defun闭合与defparameter闭包的行为不一样?

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)

countdefparameter版本中关闭但不在版本中关闭defun.为什么是这样?

Syl*_*ter 8

当你*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.

所以答案是,计数在两者中都被关闭,但在你的例子中,你创建了一个新的闭包,并且只使用了一次,多次.


Jos*_*lor 5

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调用时都会计算,每次都会返回一个新的闭包.