【问题标题】:Unexpected behavior with loop macro and closures循环宏和闭包的意外行为
【发布时间】:2013-03-30 03:12:33
【问题描述】:

为什么这些表单会这样?

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

我希望第一个 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

CL-用户>

循环宏版本是怎么回事?

【问题讨论】:

    标签: common-lisp


    【解决方案1】:

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

    使用

    (setf *closures*
      (loop for num in (list 1 2 3 4)
            collect (let ((num1 num))
                      (lambda ()
                        num1))))
    

    num1 是每次迭代的新变量。

    截至dolist,“dolist 是否在每次迭代中建立新的 var 绑定,或者是否在开始时为 var 建立一次绑定,然后在任何后续迭代中分配它,这取决于实现。” (CLHS,宏 DOLIST)。所以它可能在一种实现上有效,而在另一种实现上失败。

    【讨论】:

    • 顺便说一句,您可以将 num1 命名为 num ;) (let ((num num)) ...)
    • 我以前用过这个:(defmacro rebind (vars &amp;body body) `(let ,(mapcar #'list vars vars) ,@body))
    【解决方案2】:

    名称num 表示在评估LOOP 期间的相同绑定。 也许你想写:

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

    明白你的意思。

    【讨论】:

    • 这适用于这里的简化示例,但不适用于实际用例,我需要在循环构造中收集词法闭包。
    • @ClaytonStanley 嗯,实际上您可以将constantly 替换为(lambda (idx) ...),并将(list ...) 替换为(alexandria:iota ...)。如果有更多迭代变量,您可以添加更多列表。实际上这是 FP 方式,所以如果循环中没有状态变化,你总是可以使用它...
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多