为什么这些形式表现如此?
CL-USER>
(setf *closures*
(loop for num in (list 1 2 3 4)
collect (lambda ()
num)))
(
#<COMPILED-LEXICAL-CLOSURE #x302004932E1F>
#<COMPILED-LEXICAL-CLOSURE #x302004932DCF>
#<COMPILED-LEXICAL-CLOSURE #x302004932D7F>
#<COMPILED-LEXICAL-CLOSURE #x302004932D2F>)
CL-USER>
(funcall (first *closures*))
4
CL-USER>
(funcall (second *closures*))
4
Run Code Online (Sandbox Code Playgroud)
我原本期望第一个funcall返回1,第二个返回2,等等.这个行为与Clozure Common Lisp和Steel-Bank Common Lisp实现一致.
如果我将循环宏重写为使用dolist的版本,我期望的是返回的内容:
(setf *closures*
(let ((out))
(dolist (item (list 1 2 3 4) (reverse out))
(push (lambda () item) out))))
(
#<COMPILED-LEXICAL-CLOSURE #x302004A12C4F>
#<COMPILED-LEXICAL-CLOSURE #x302004A12BFF>
#<COMPILED-LEXICAL-CLOSURE #x302004A12BAF>
#<COMPILED-LEXICAL-CLOSURE #x302004A12B5F>)
CL-USER>
(funcall (first *closures*))
1
CL-USER>
(funcall (second *closures*))
2
Run Code Online (Sandbox Code Playgroud)
CL-USER>
循环宏版本发生了什么?
num
是所有lambdas共享的相同变量.
使用
(setf *closures*
(loop for num in (list 1 2 3 4)
collect (let ((num1 num))
(lambda ()
num1))))
Run Code Online (Sandbox Code Playgroud)
num1
每次迭代都是新变量.
截至dolist
,"它是依赖于实现的,是否dolist在每次迭代时建立var的新绑定,或者它是否在开始时为var建立一次绑定,然后在任何后续迭代中分配它." (CLHS,Macro DOLIST).所以它可能适用于一个实现而另一个实现失败.