为什么Lua没有“继续”声明?

时间:2010-08-19 18:24:54

标签: loops lua language-design

在过去的几个月里,我一直在与Lua打交道,我非常喜欢大部分功能,但我仍然遗漏了其中的一些内容:

  • 为什么没有continue
  • 它有什么变通方法?

11 个答案:

答案 0 :(得分:63)

语言管理词汇范围的方式会产生包含gotocontinue的问题。例如,

local a=0
repeat 
    if f() then
        a=1 --change outer a
    end
    local a=f() -- inner a
until a==0 -- test inner a

循环体内local a的声明掩盖了名为a的外部变量,并且该局部的范围扩展到until语句的条件,因此条件正在测试最里面的a

如果存在continue,则必须在语义上将其限制为仅在条件中使用的所有变量进入范围之后才有效。这是向用户记录并在编译器中强制执行的困难条件。已经讨论了围绕这个问题的各种提案,包括使用continue样式循环禁止repeat ... until的简单答案。到目前为止,没有一个具有足够引人注目的用例来将它们包含在语言中。

解决方法通常是反转导致continue执行的条件,并在该条件下收集循环体的其余部分。那么,以下循环

-- not valid Lua 5.1 (or 5.2)
for k,v in pairs(t) do
  if isstring(k) then continue end
  -- do something to t[k] when k is not a string
end

可以写

-- valid Lua 5.1 (or 5.2)
for k,v in pairs(t) do
  if not isstring(k) then 
    -- do something to t[k] when k is not a string
  end
end

很明显,除非你有一系列精心设计的剔除来控制循环操作,否则通常不会有负担。

答案 1 :(得分:62)

在Lua 5.2中,最好的解决方法是使用goto:

-- prints odd numbers in [|1,10|]
for i=1,10 do
  if i % 2 == 0 then goto continue end
  print(i)
  ::continue::
end

自2.0.1版以来,LuaJIT支持此功能

答案 2 :(得分:37)

您可以将循环体包装在其他repeat until true中,然后在内部使用do break end以获得继续效果。当然,如果你还打算真正break出现循环,你还需要设置额外的标志。

这将循环5次,每次打印1,2和3。

for idx = 1, 5 do
    repeat
        print(1)
        print(2)
        print(3)
        do break end -- goes to next iteration of for
        print(4)
        print(5)
    until true
end

这种结构甚至可以转换为Lua字节码中的文字一个操作码JMP

$ luac -l continue.lua 

main <continue.lua:0,0> (22 instructions, 88 bytes at 0x23c9530)
0+ params, 6 slots, 0 upvalues, 4 locals, 6 constants, 0 functions
    1   [1] LOADK       0 -1    ; 1
    2   [1] LOADK       1 -2    ; 3
    3   [1] LOADK       2 -1    ; 1
    4   [1] FORPREP     0 16    ; to 21
    5   [3] GETGLOBAL   4 -3    ; print
    6   [3] LOADK       5 -1    ; 1
    7   [3] CALL        4 2 1
    8   [4] GETGLOBAL   4 -3    ; print
    9   [4] LOADK       5 -4    ; 2
    10  [4] CALL        4 2 1
    11  [5] GETGLOBAL   4 -3    ; print
    12  [5] LOADK       5 -2    ; 3
    13  [5] CALL        4 2 1
    14  [6] JMP         6   ; to 21 -- Here it is! If you remove do break end from code, result will only differ by this single line.
    15  [7] GETGLOBAL   4 -3    ; print
    16  [7] LOADK       5 -5    ; 4
    17  [7] CALL        4 2 1
    18  [8] GETGLOBAL   4 -3    ; print
    19  [8] LOADK       5 -6    ; 5
    20  [8] CALL        4 2 1
    21  [1] FORLOOP     0 -17   ; to 5
    22  [10]    RETURN      0 1

答案 3 :(得分:16)

第一部分在FAQ中被解答为slain指出。

至于解决方法,你可以在一个函数和return之前将循环体包裹起来,例如。

-- Print the odd numbers from 1 to 99
for a = 1, 99 do
  (function()
    if a % 2 == 0 then
      return
    end
    print(a)
  end)()
end

或者,如果您同时需要breakcontinue功能,请让本地函数执行测试,例如

local a = 1
while (function()
  if a > 99 then
    return false; -- break
  end
  if a % 2 == 0 then
    return true; -- continue
  end
  print(a)
  return true; -- continue
end)() do
  a = a + 1
end

答案 4 :(得分:15)

Straight from the designer of Lua himself

  

我们对“继续”的主要关注是,有一些其他控制结构(在我们看来)或多或少与“继续”一样重要,甚至可能取而代之。 (例如,打破标签[在Java中]或甚至更通用的goto。)“continue”似乎并不比其他控制结构机制更特殊,除了它存在于更多语言中。 (Perl实际上有两个“continue”语句,“next”和“redo”。两者都很有用。)

答案 5 :(得分:8)

我之前从未使用过Lua,但我用Google搜索并想出了这个:

http://www.luafaq.org/

检查question 1.26

  

这是一个常见的抱怨。 Lua的作者认为,继续只是众多可能的新控制流机制中的一种(事实上它不能与重复范围规则一起使用/直到次要因素。)

     

在Lua 5.2中,有一个goto语句,可以很容易地用来做同样的工作。

答案 6 :(得分:6)

Lua是一种轻量级的脚本语言,希望尽可能缩小。例如,许多一元运算(例如前后递增)不可用

您可以使用goto like代替

arr = {1,2,3,45,6,7,8}
for key,val in ipairs(arr) do
  if val > 6 then
     goto skip_to_next
  end
     # perform some calculation
  ::skip_to_next::
end

答案 7 :(得分:5)

我们可以实现如下,它会跳过偶数

local len = 5
for i = 1, len do
    repeat 
        if i%2 == 0 then break end
        print(" i = "..i)
        break
    until true
end

O / P:

i = 1
i = 3
i = 5

答案 8 :(得分:4)

我们多次遇到过这种情况,我们只是使用一个标志来模拟继续。我们也试图避免使用goto语句。

示例:代码打算从i = 1到i = 10打印语句,除了i = 3。此外,它还打印&#34;循环开始&#34;循环结束&#34;,&#34;如果开始&#34;,&#34;如果结束&#34;模拟代码中存在的其他嵌套语句。

size = 10
for i=1, size do
    print("loop start")
    if whatever then
        print("if start")
        if (i == 3) then
            print("i is 3")
            --continue
        end
        print(j)
        print("if end")
    end
    print("loop end")
end

是通过将所有剩余的语句包含在带有测试标志的循环结束范围之前实现的。

size = 10
for i=1, size do
    print("loop start")
    local continue = false;  -- initialize flag at the start of the loop
    if whatever then
        print("if start")
        if (i == 3) then
            print("i is 3")
            continue = true
        end

        if continue==false then          -- test flag
            print(j)
            print("if end")
        end
    end

    if (continue==false) then            -- test flag
        print("loop end")
    end
end

我并不是说这是最好的方法,但它对我们来说非常有效。

答案 9 :(得分:3)

再次使用反转,您只需使用以下代码:

for k,v in pairs(t) do
  if not isstring(k) then 
    -- do something to t[k] when k is not a string
end

答案 10 :(得分:-1)

为什么没有继续?

因为这是不必要的。开发人员很少需要它。

A)当您有一个非常简单的循环(例如1或2线)时,您只需扭转循环条件,它仍然可读性很强。

B)在编写简单的过程代码(也就是上个世纪我们编写代码的方式)时,还应该应用结构化编程(又就是上个世纪我们编写更好的代码的方式)

C)如果您正在编写面向对象的代码,则循环主体应包含不超过一或两个方法调用,除非它可以用一或两行表示(在这种情况下,请参阅A)

D)如果您正在编写功能代码,只需为下一次迭代返回一个简单的尾调用即可。

唯一要使用continue关键字的情况是,如果要像Lua一样编写Lua的代码,

有什么解决方法?

除非A)适用,在这种情况下,不需要任何变通办法,您应该进行结构化,面向对象或函数式编程。这些就是Lua构建的范式,因此,如果您竭尽所能避免使用它们的模式,那么您将与该语言作斗争。