公共 Lisp 循环

Stu*_*ent 4 loops common-lisp

在以下循环中:

(let ((funs (loop for i upto 3 do (print i) collect #'(lambda () i))))
  (loop for fun in funs collect (funcall fun)))
Run Code Online (Sandbox Code Playgroud)

我直觉地认为我会得到一个包含四个闭包的列表,它们在被调用时返回数字 0 1 2 和 3,但这就是我得到的:

>> 0 
>> 1 
>> 2 
>> 3
=> (4 4 4 4)
Run Code Online (Sandbox Code Playgroud)

但是将i本地重新绑定到其他东西:

(let ((funs (loop for i upto 3 do (print i) collect (let ((x i))
                          #'(lambda () x)))))
  (loop for fun in funs collect (funcall fun)))
Run Code Online (Sandbox Code Playgroud)

按预期工作:

>> 0 
>> 1 
>> 2 
>> 3
=> (0 1 2 3)
Run Code Online (Sandbox Code Playgroud)

所以每个函数都返回 4,为什么所有返回值都相同,为什么是 4?

更新

这实际上似乎是一个关于 lambda 的问题。见下文:

(setq dynamic-var 8
  funs ())
(push (lambda () dynamic-var) funs)
(incf dynamic-var)
(push (lambda () dynamic-var) funs)
(mapcar #'funcall funs)         ;(9 9)
Run Code Online (Sandbox Code Playgroud)

Rai*_*wig 8

有什么作用

(let (i      ; a counter variable
      f)     ; a list of functions
  (setf i 1)
  (push (lambda () i) f)
  (setf i 2)
  (push (lambda () i) f)

  (mapcar #'funcall f))
Run Code Online (Sandbox Code Playgroud)

返回?

怎么样:

(let (i
      f)
  (setf i 1)
  (push (lambda () i) f)
  (let (i)
    (setf i 2)
    (push (lambda () i) f))

  (mapcar #'funcall f))
Run Code Online (Sandbox Code Playgroud)

哪个是LOOP型号?

也可以看看:

CL-USER 42 > (let (f)
               (dotimes (i 10 (mapcar #'funcall (reverse f)))
                 (push (lambda () i) f)))
(10 10 10 10 10 10 10 10 10 10)

CL-USER 43 > (let (f)
               (dotimes (i 10 (mapcar #'funcall (reverse f)))
                 (push (let ((i i))
                         (lambda () i))
                       f)))
(0 1 2 3 4 5 6 7 8 9)
Run Code Online (Sandbox Code Playgroud)

Common Lisp 标准说DOTIMES

dotime 是在每次迭代时建立新的 var 绑定,还是在开始时为 var 建立一次绑定,然后在任何后续迭代中分配它,这取决于实现。

你写:

我直觉地认为我会得到一个包含四个闭包的列表,它们在被调用时返回数字 0 1 2 和 3

这种直觉只是部分正确。你会得到四个闭包,但在这种情况下,它们都共享一个变量绑定。因此他们只能看到这个变量的当前绑定。在 Common Lisp 中,这个绑定是可变的,闭包看到的是当前绑定,而不是闭包创建时的初始绑定之一。

当每个闭包都有自己的变量绑定时,您的直觉是正确的。

附加答案:为什么这个 Lisp 返回 10 ?

(PROGN
  (SETQ I (THE INTEGER (1+ (THE INTEGER I))))
  (WHEN (>= (THE INTEGER I)
            (THE INTEGER #:|dotimes-count-1075|))
    (GO #:|dotimes-end-tag1080|)))
Run Code Online (Sandbox Code Playgroud)

以上是dotimes构造宏展开的一部分。如您所见,它首先增加变量,然后测试>=. 因此退出时I>=10。因此的最后一个值I就是10。稍后退出后,dotimes您将检索 的值,I然后是10