我应该避免“手动”重置自动递增的循环变量吗?

Tho*_*hel 6 lisp variables loops common-lisp

我有一个包含多个变量的循环;其中之一在每一步递增。然而,有时这个变量可能会被重置为 0。因此我可以写:

(loop
    with z = 0
    ...
    do (progn
           (setq (z (1+ z)))
           ...
           (if ...
               (setq z 0))))
Run Code Online (Sandbox Code Playgroud)

但看起来我也可以写:

(loop
    for z from 1
    ...
    do (progn
           ...
           (if ...
               (setq z 0))))
Run Code Online (Sandbox Code Playgroud)

第二个版本更短(减少一行);但我不确定它是否真的干净;它是否完全符合 Lisp 标准?

Rai*_*wig 6

另一种可能:

(loop for z = 0 then (if ... 0 (1+ z))
      ...
      )
Run Code Online (Sandbox Code Playgroud)


小智 6

我认为你应该避免这样做,原因有二。

首先,据我所知,loop规范没有明确说明这是否安全,但dotimes例如说

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

换句话说

(dotimes (i n)
  ... my code ...)
Run Code Online (Sandbox Code Playgroud)

可能会扩展到类似的东西

(let (...)
  ...
  (tagbody
   start
   (let ((i ...))
     ...my code...)
   (unless ... (go start))
   ...))
Run Code Online (Sandbox Code Playgroud)

例如。

我非常强烈地怀疑loop实现在实践中不会这样做,我认为你可能会争辩说规范暗示他们不允许这样做,但我也认为措辞不够清楚,你想要取决于那个。

其次,我认为在迭代主体内调整迭代变量值的风格问题是,好吧,太可怕了:如果有人阅读,(loop for i from 0 below 100 do ...)他们希望 i 从 0 到 99 的整数步进,而不是因为某些狡猾而随机跳跃代码中的晦涩。所以我个人会避免这种情况,因为它是一种风格问题,而不是确保规范允许或不说它是安全的。

相反,我会按照 Rainer 的建议去做,(loop for x = ... then ...) 很明显,x它的值由您的代码决定,而不是由loop.

最后请注意,您可以通过例如:

> (mapcar #'funcall (loop for i below 3
                                    collect (lambda () i)))
(3 3 3)
Run Code Online (Sandbox Code Playgroud)

在这种情况下,我使用的实现没有,在这种情况下重新绑定它(但它当然可能在其他情况下这样做!)。


还值得注意的是,分配给循环控制变量的可能性阻止了重要的优化成为可能:循环展开。如果系统不能假设3 次(loop for i from 0 to 2 do ...)求值,并且是适当的值,则您无法真正展开这样的循环。...i