Loops 为什么Lua没有;“继续”;陈述

Loops 为什么Lua没有;“继续”;陈述,loops,lua,language-design,Loops,Lua,Language Design,在过去的几个月里,我一直在与Lua打交道,我真的很喜欢其中的大部分功能,但我仍然缺少一些功能: 为什么没有继续 有什么解决办法 我以前从未使用过Lua,但我在谷歌上搜索了一下,得出了以下结论: 检查 这是常见的抱怨。Lua的作者认为continue只是许多可能的新控制流机制中的一个(它不能与repeat/until的范围规则一起工作这一事实是次要因素) 在Lua5.2中,有一个goto语句,可以很容易地用于完成相同的工作 该语言管理词法范围的方式会产生问题,包括goto和continue。比

在过去的几个月里,我一直在与Lua打交道,我真的很喜欢其中的大部分功能,但我仍然缺少一些功能:

  • 为什么没有
    继续
  • 有什么解决办法

我以前从未使用过Lua,但我在谷歌上搜索了一下,得出了以下结论:

检查

这是常见的抱怨。Lua的作者认为continue只是许多可能的新控制流机制中的一个(它不能与repeat/until的范围规则一起工作这一事实是次要因素)

在Lua5.2中,有一个goto语句,可以很容易地用于完成相同的工作


该语言管理词法范围的方式会产生问题,包括
goto
continue
。比如说,

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
的外部变量,该局部变量的范围扩展到
语句的整个条件,直到
语句,因此该条件正在测试最里面的
a

如果存在
continue
,则必须在语义上对其进行限制,使其仅在条件中使用的所有变量进入范围后才有效。这是一个很难向用户记录并在编译器中强制执行的条件。围绕这个问题的各种建议已经被讨论过,包括简单的答案是不允许
继续
重复。。。直到循环的样式。到目前为止,还没有一个具有足够说服力的用例将它们包含在语言中

解决方法通常是反转将导致执行
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
这已经足够清楚了,而且通常不会成为负担,除非您有一系列精细的剔除来控制循环操作。

:

我们对“继续”的主要担忧是,在我们看来,还有一些其他控制结构或多或少与“继续”同等重要,甚至可能取代它。(例如,与标签(如Java中的)或更通用的goto决裂。)“continue”似乎并不比其他控制结构机制更特殊,只是它存在于更多的语言中。(Perl实际上有两个“continue”语句,“next”和“redo”。这两个语句都很有用。)


如前所述,第一部分在本文中得到了回答

至于解决方法,您可以将循环体包装在函数中,然后尽早返回,例如

-- 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
或者,如果您同时需要
中断
继续
功能,请让本地功能执行测试,例如:

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

同样,对于反转,您可以简单地使用以下代码:

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

在Lua5.2中,最好的解决方法是使用goto:

——打印[|1,10 |]中的奇数
对于i=1,10 do
如果i%2==0,则转到继续结束
印刷品(一)
::继续::
结束

从2.0.1版开始,LuaJIT就支持这一点。您可以在附加的
中包装循环体,重复直到为true,然后在内部使用
do break end
以实现继续的效果。当然,如果您还打算真正地
中断
循环,则需要设置额外的标志

这将循环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
$luac-l continue.lua
主(22条指令,0x23c9530处88字节)
0+参数,6个插槽,0个上限值,4个局部变量,6个常量,0个函数
1[1]荷载K 0-1;1.
2[1]荷载K 1-2;3.
3[1]荷载K 2-1;1.
4[1]为0 16;到21岁
5[3]GETGLOBAL 4-3;打印
6[3]荷载K 5-1;1.
7[3]致电4 2 1
8[4]GETGLOBAL 4-3;打印
9[4]荷载K 5-4;2.
10[4]致电4 2 1
11[5]GETGLOBAL 4-3;打印
12[5]荷载K 5-2;3.
13[5]致电4 2 1
14[6]jmp6;到21号--给你!若从代码中删除do break end,结果将只相差这一行。
15[7]GETGLOBAL 4-3;打印
16[7]荷载K 5-5;4.
17[7]致电4 2 1
18[8]GETGLOBAL 4-3;打印
19[8]荷载K 5-6;5.
20[8]致电4 2 1
21[1]用于循环0-17;至5
22[10]返回0 1

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

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:


我们多次遇到这种情况,只需使用一个标志来模拟“继续”。我们也尽量避免使用goto语句

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

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
我并不是说这是最好的方法,但它对我们非常有效。

为什么没有继续? 因为它是不必要的。很少有开发人员需要它的情况

A) 当你有一个非常简单的循环时,比如说一个1行或2行的循环,那么你只需要改变循环条件,它仍然是可读的

B) 当你编写简单的过程代码(又名,我们在上个世纪如何编写代码)时,你也应该应用结构化编程(又名,我们在上个世纪如何编写更好的代码)

C) 如果您正在编写面向对象的代码,那么循环体应该由不超过一个或两个方法调用组成,除非它可以用一个或两个线性函数表示(在这种情况下,请参阅a)

D) 如果您正在编写函数代码,只需为下一次迭代返回一个简单的尾部调用

只有在这种情况下,您才需要使用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
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