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

时间:2013-11-12 02:43:34

标签: lisp closures common-lisp

考虑这两个:

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

3 个答案:

答案 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都会返回一个新的闭包。