为什么 Lua 中的 for 循环限制不是动态计算的?

这里是一个基本的 for 循环

local a = {"first","second","third","fourth"}
for i=1,#a do
    print(i.."th iteration")
    a = {"first"}
end

目前,该循环执行了四次迭代。

for 循环限制应该动态计算吗?如果它是动态计算的,第一次迭代后 #a 将为 1,for 循环将会断开...

这样不是更有意义吗? 或者,这种情况有特定的原因吗?

点赞
用户19784
用户19784

长度是在 for 循环初始化时计算一次的。它不会在每次循环中重新计算 - for 循环用于从起始值迭代到结束值。如果你希望如果数组被重新赋值就提前终止循环,你可以编写自己的循环代码:

local a = {"first", "second", "third", "fourth"}

function process_array (fn)
   local inner_fn
   inner_fn =
      function (ii)
         if ii <= #a then
            fn(ii,a)
            inner_fn(1 + ii)
         end
      end
   inner_fn(1, a)
end

process_array(function (ii)
                    print(ii.."th iteration: "..a[ii])
                    a = {"first"}
                     end)
2012-11-04 00:54:41
用户1008957
用户1008957

主要原因为什么数值 for 循环的限制只计算一次,最有可能是出于性能方面的考虑。

按照当前的行为,你可以在 for 循环限制中放置任意复杂的表达式,包括函数调用,而不会有性能损失。例如:

local prod = 1
for i = computeStartLoop(), computeEndLoop(), computeStep() do
  prod = prod * i
end

如果 computeEndLoopcomputeStep 需要在每次迭代中被调用,上述代码将非常慢。

如果标准 Lua 解释器,尤其是 LuaJIT,与其他脚本语言相比如此之快,那是因为许多 Lua 特性都是为了考虑性能而设计的。


在极少数情况下,如果单次评估行为不可取,使用 while endrepeat until 用通用循环替换 for 循环很容易。

local prod = 1
local i = computeStartLoop()
while i <= computeEndLoop() do
  prod = prod * i
  i = i + computeStep()
end
2012-11-04 06:28:49
用户204011
用户204011

Performance is a good answer but I think it also makes the code easier to understand and less error-prone. Also, that way you can (almost) be sure that a for loop always terminates.

性能是一个好的答案,但我认为这样也可以使代码更易于理解和减少出错的几率。此外,这种方式你(几乎)可以确信一个 for 循环总是会终止。

Think about what would happen if you wrote that instead:

想象一下如果你写成这样会发生什么:

local a = {"first","second","third","fourth"}
for i=1,#a do
    print(i.."th iteration")
    if i > 1 then a = {"first"} end
end

你如何理解 for i=1,#a?它是一个等式比较(当 i==#a 时停止)还是一个不等式比较(当 i>=#a 时停止)?在每种情况下会发生什么结果?

你应该将 Lua 的 for 循环看作是在序列上进行迭代,就像利用 (x)range 的 Python 习惯用法一样:

a = ["first", "second", "third", "fourth"]
for i in range(1,len(a)+1):
    print(str(i) + "th iteration")
    a = ["first"]

如果你想要每次评估条件,只需使用 while

local a = {"first","second","third","fourth"}
local i = 1
while i <= #a do
    print(i.."th iteration")
    a = {"first"}
    i = i + 1
end
2012-11-05 17:21:16