Common lisp 循环宏和闭包的意外行为

Common lisp 循环宏和闭包的意外行为,common-lisp,Common Lisp,为什么这些表单会有这样的行为 CL-USER> (setf *closures* (loop for num in (list 1 2 3 4) collect (lambda () num))) ( #<COMPILED-LEXICAL-CLOSURE #x302004932E1F> #<COMPILED-LEXICAL-CLOSURE #x302004932DCF> #&l

为什么这些表单会有这样的行为

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
CL-USER>
(setf*关闭*
(列表1 2 3 4中的num循环)
收集(λ()
num)))
(     
#
#
#
#)
CL-USER>
(funcall(第一个*闭包*)
4.
CL-USER>
(funcall(第二个*闭包*)
4.
我希望第一个funcall返回1,第二个funcall返回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
(setf*闭包)*
(放((出去))
(dolist(项目(清单1、2、3、4)(反向输出))
(推出(lambda()项)))
(
#
#  
#
#)
CL-USER>
(funcall(第一个*闭包*)
1.
CL-USER>
(funcall(第二个*闭包*)
2.
CL-USER>


循环宏版本是怎么回事?

num
是所有lambda共享的同一个变量

使用

num1
是每个迭代的新变量


截至
dolist
,“dolist是在每次迭代中建立新的var绑定,还是在开始时为var建立一次绑定,然后在任何后续迭代中分配,这取决于实现。”(CLHS,宏dolist)。因此,它可能在一个实现上工作,而在另一个实现上失败。

名称
num
表示循环求值期间的相同绑定。 也许你想写:

(mapcar 'constantly (list 1 2 3 4))

为了理解你的意思。

顺便说一句,你可以把
num1
命名为
num
;)(let((num num))…)我以前使用过:
(defmacro rebind(vars和body body)`(let,(mapcar#list vars vars),@body))
这适用于这里的简化示例,但不适用于实际用例,我需要在循环构造中收集词汇闭包。@ClaytonStanley-Hmm,实际上,您可以不断地将
替换为
(lambda(idx)…
,并将
(列表…
)替换为
(亚历山大:物联网…
)。如果有更多的迭代变量,您可以添加更多的列表。实际上,这是FP方式,所以如果循环中没有状态改变,您可以始终使用它。。。
(mapcar 'constantly (list 1 2 3 4))