2

考虑这两个:

(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版本中。为什么是这样?

4

3 回答 3

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.

所以答案是, count 在两者中都已关闭,但在您的示例中,您正在创建一个新的闭包并且只使用一次,多次。

于 2013-11-12T03:02:42.713 回答
5

Sylwester 的回答很好地解释了这一点,但在一个具有更明确副作用的示例使这一点更清楚的情况下,请考虑:

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

于 2013-11-12T03:23:48.057 回答
3

的值*lfn*是一个闭包。

testclosure每次调用该函数时都会返回一个新的闭包。

于 2013-11-12T10:51:37.883 回答