В последние несколько месяцев я много общался с Lua, и мне очень нравятся большинство функций, но я все еще не вижу что-то среди них:
- Почему нет
continue
? - Какие обходные пути существуют для этого?
В последние несколько месяцев я много общался с Lua, и мне очень нравятся большинство функций, но я все еще не вижу что-то среди них:
continue
?Способ управления лексической областью языка создает проблемы с включением как 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
, и область действия этого локального объекта распространяется на условие оператора 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
Это достаточно ясно, и обычно это не бремя, если у вас нет серии сложных отбросов, которые управляют работой цикла.
В 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
Это поддерживается в LuaJIT с версии 2.0.1
Вы можете обернуть тело цикла дополнительным 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
Эта конструкция даже переводит на буквенный один код операции JMP
в байт-код Lua!
$ 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
, выполните локальную функцию, например,
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:
Наша основная забота о "продолжении" заключается в том, что существует несколько других структур управления, которые (на наш взгляд) более или менее столь же важны, как и "продолжают" и могут даже заменить его. (Например, разрыв с метками [как в Java] или даже более общий goto.) "Continue" не кажется более особенным, чем другие механизмы структуры управления, за исключением того, что он присутствует на более языках. (Perl на самом деле имеет два оператора "continue", "next" и "redo". Оба полезны.)
Я никогда раньше не использовал Lua, но я искал его и придумал следующее:
Отметьте вопрос 1.26.
Это распространенная жалоба. Авторы Lua считают, что продолжение является лишь одним из нескольких возможных новых механизмов управления потоком (тот факт, что он не может работать с правилами области повторения/до, был второстепенным фактором.)
В Lua 5.2 существует инструкция goto, которую можно легко использовать для выполнения той же работы.
Мы можем достичь этого, как показано ниже, он пропустит четные числа
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
достигается путем включения всех оставшихся операторов до конечной области цикла с флагом теста.
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
Я не говорю, что это лучший подход, но он отлично работает для нас.
Снова с инвертированием вы можете просто использовать следующий код:
for k,v in pairs(t) do
if not isstring(k) then
-- do something to t[k] when k is not a string
end