Почему у Lua нет инструкции "продолжить"?

В последние несколько месяцев я много общался с Lua, и мне очень нравятся большинство функций, но я все еще не вижу что-то среди них:

  • Почему нет continue?
  • Какие обходные пути существуют для этого?

Ответ 1

Способ управления лексической областью языка создает проблемы с включением как 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

Это достаточно ясно, и обычно это не бремя, если у вас нет серии сложных отбросов, которые управляют работой цикла.

Ответ 2

В 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

Ответ 3

Вы можете обернуть тело цикла дополнительным 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

Ответ 4

На первую часть ответят 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

Ответ 5

Прямо от дизайнера самого Lua:

Наша основная забота о "продолжении" заключается в том, что существует несколько других структур управления, которые (на наш взгляд) более или менее столь же важны, как и "продолжают" и могут даже заменить его. (Например, разрыв с метками [как в Java] или даже более общий goto.) "Continue" не кажется более особенным, чем другие механизмы структуры управления, за исключением того, что он присутствует на более языках. (Perl на самом деле имеет два оператора "continue", "next" и "redo". Оба полезны.)

Ответ 6

Я никогда раньше не использовал Lua, но я искал его и придумал следующее:

http://www.luafaq.org/

Отметьте вопрос 1.26.

Это распространенная жалоба. Авторы Lua считают, что продолжение является лишь одним из нескольких возможных новых механизмов управления потоком (тот факт, что он не может работать с правилами области повторения/до, был второстепенным фактором.)

В Lua 5.2 существует инструкция goto, которую можно легко использовать для выполнения той же работы.

Ответ 7

Мы можем достичь этого, как показано ниже, он пропустит четные числа

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

Мы сталкивались с этим сценарием много раз, и мы просто используем флаг для имитации продолжения. Мы также стараемся избегать использования операторов 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

Я не говорю, что это лучший подход, но он отлично работает для нас.

Ответ 9

Снова с инвертированием вы можете просто использовать следующий код:

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