2

次の 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*1つのクロージャー内で関数を作成しています..それを呼び出すと、クローズされたオーバーカウントが増加し、それに評価されます。

testclosure*lfm*呼び出されるたびに、 for で行ったことと同じことを行います。したがって:

(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'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)は 1 回評価されるため、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 に答える