为什么Lua没有“继续”的说法?
我在过去几个月里一直和Lua打交道,而且我非常喜欢大部分function,但是我仍然错过了其中的一些东西:
- 为什么没有
continue
? - 有什么解决方法呢?
语言pipe理词汇范围的方式造成了包括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
的外部variables,并且该局部范围扩展到了until
语句的条件,所以条件是testing最内部的a
。
如果continue
存在,则只有在条件中使用的所有variables已经进入范围之后,才能在语义上受到限制。 这是向用户提供文档并在编译器中执行的一个困难条件。 围绕这个问题的各种build议已经被讨论过,包括简单的答复,不要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
这是很清楚的,通常不是一个负担,除非你有一系列控制循环操作的精心挑选。
在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中得到了支持
你可以在循环体中joinrepeat 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
第一部分在FAQ中被解释为被杀的人指出的。
至于解决方法,你可以将循环的主体包装在一个函数中,并且尽早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
或者,如果你想同时break
和continue
function,让本地function执行testing,例如
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
直接从Lua的devise师本人 :
我们主要关注“继续”的是,还有其他几个控制结构(在我们看来)与“继续”或多或less一样重要,甚至可能取而代之。 (例如,打破标签(如Java),甚至是更通用的转换。)除了以更多的语言存在之外,“继续”似乎不像其他控制结构机制那么特殊。 (Perl实际上有两个“继续”语句,“下一个”和“重做”,两者都是有用的。)
我以前从来没有用过Lua,但是我用Googlesearch了这个:
检查问题1.26 。
这是一个常见的投诉。 Lua的作者认为,继续只是许多可能的新的控制stream动机制之一(事实上它不能适用范围规则的重复/直到是次要因素)。
在Lua 5.2中,有一个很容易用来做同样工作的goto语句。
再次反转,你可以简单地使用下面的代码:
for k,v in pairs(t) do if not isstring(k) then -- do something to t[k] when k is not a string end
我们可以做到如下,它会跳过偶数
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
我们多次遇到这种情况,我们只是使用一个标志来模拟继续。 我们尽量避免使用goto语句。
例如:代码打算打印1到10之间的数字,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
是通过用一个testing标志封闭所有剩余的语句直到循环的结束范围来实现的。
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
我并不是说这是最好的方法,但它对我们来说是完美的。