Почему у Луа нет «продолжения»? - PullRequest
130 голосов
/ 19 августа 2010

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

  • Почему нет continue?
  • Какие есть обходные пути?

Ответы [ 9 ]

63 голосов
/ 17 октября 2012

В 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

63 голосов
/ 20 августа 2010

То, как язык управляет лексической областью, создает проблемы, включая 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 loop. Пока что ни у одного из них не было достаточно убедительных вариантов использования, чтобы включить их в язык.

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

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

37 голосов
/ 11 декабря 2012

Вы можете обернуть тело цикла дополнительно 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
16 голосов
/ 25 мая 2011

Ответ на первую часть в 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
15 голосов
/ 24 мая 2011

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

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

8 голосов
/ 19 августа 2010

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

http://www.luafaq.org/

Проверка вопрос 1.26 .

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

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

5 голосов
/ 11 сентября 2014

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

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
4 голосов
/ 03 августа 2016

Мы сталкивались с этим сценарием много раз, и мы просто используем флаг для имитации продолжения.Мы также стараемся избегать использования операторов goto.

Пример: код предназначен для вывода операторов с i = 1 до i = 10, за исключением i = 3.Кроме того, он также печатает «начало цикла», «конец цикла», «если начало» и «если конец» для имитации других вложенных операторов, существующих в вашем коде.операторы до конечной области видимости цикла с флагом проверки.

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

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

3 голосов
/ 17 октября 2012

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

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