2010-08-19 21 views
110

he estado tratando mucho con Lua en los últimos meses, y me gusta mucho la mayor parte de las características, pero todavía estoy perdiendo algo entre los que:¿Por qué Lua no tiene una declaración de "continuación"?

  • Por qué no hay continue?
  • ¿Qué soluciones hay para eso?
+10

Dado que se formuló esta pregunta, Lua obtuvo una declaración 'goto' que se puede usar para implementar continue. Vea las respuestas a continuación. – lhf

Respuesta

4

nunca he utilizado Lua antes, pero lo busqué en Google y se acercó con esto:

http://www.luafaq.org/

Comprobar question 1.26.

Esto es una queja común. Los autores de Lua consideraron que continuar era solo uno de varios posibles nuevos mecanismos de flujo de control (el hecho de que no puede funcionar con las reglas de alcance de repeat/until era un factor secundario.)

En Lua 5.2, hay un declaración goto que se puede usar fácilmente para hacer el mismo trabajo.

57

La forma en que maneja el idioma ámbito léxico crea problemas con la inclusión de ambos goto y continue. Por ejemplo,

local a=0 
repeat 
    if f() then 
     a=1 --change outer a 
    end 
    local a=f() -- inner a 
until a==0 -- test inner a 

La declaración de local a dentro de las máscaras cuerpo del bucle la variable exterior nombra a, y el alcance de que locales extiende a través de la condición de la declaración until lo que la condición está probando el más interior a.

Si existiera continue, tendría que restringirse semánticamente para que solo sea válida después de que todas las variables utilizadas en la condición hayan entrado en el ámbito. Esta es una condición difícil de documentar para el usuario y hacer cumplir en el compilador. Se han discutido varias propuestas sobre este tema, incluida la respuesta simple de no permitir continue con el estilo repeat ... until del ciclo. Hasta ahora, ninguno ha tenido un caso de uso suficientemente convincente para incluirlos en el lenguaje.

La solución alternativa es invertir la condición que provocaría la ejecución de continue, y recopilar el resto del cuerpo del bucle en esa condición. Por lo tanto, el siguiente bucle

-- 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 

podría escribirse

-- 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 

Es bastante claro, y por lo general no una carga a menos que tenga una serie de sacrificios elaborados que controlan el funcionamiento del bucle.

+3

Viniendo de un fondo de python esta es una respuesta confusa porque cada alcance ya conoce sus variables locales antes de ejecutar. Es decir. Esperaba un error de variable local independiente en el caso de alcanzar 'hasta ...'. – ubershmekel

+0

No sé casi nada sobre Python. Lua tiene una visión muy estrecha del alcance de las variables locales: se controla léxicamente.El alcance está limitado al final del bloque que contiene la variable. La peculiaridad es que la condición de la cláusula 'a menos' es * dentro * del bloque léxicamente, porque hace más claros los casos habituales. El ejemplo anterior con una variable local "a" sombreando una variable global "a" muestra un caso en el que ese límite léxico invisible puede ser confuso. Lua 5.2 introduce 'goto', pero requiere que no se use de una manera que rompa el alcance léxico. – RBerteig

+0

Creo que hace un buen punto, pero cuando dice "Si continúo existiendo, tendría que restringirse semánticamente para que solo sea válido después de que todas las variables utilizadas en la condición hayan entrado en alcance". ¿Podría explicar por qué esto debe hacerse cumplir? No veo por qué una búsqueda en "inner a" simplemente no podría dar como resultado ningún otro símbolo "no definido" en el alcance actual (que es lo que esperaba). – udoprog

12

Straight from the designer of Lua himself:

Nuestra principal preocupación con "continuar" es que hay varias otras estructuras de control que (en nuestra opinión) son más o menos tan importante como "continuar" e incluso le sustituya. (Por ejemplo, romper con las etiquetas [como en Java] o incluso un goto más genérico.) "Continuar" no parece más especial que otros mecanismos de estructura de control, excepto que está presente en más idiomas. (Perl en realidad tiene dos declaraciones "continuar", "siguiente" y "rehacer". Ambas son útiles.)

+0

Me encanta la admisión: "Ambos son útiles" justo después de una explicación de "no vamos a hacerlo" –

+0

Fue para señalar el alcance que ellos * estaban * buscando para abordar cuando * sí * lo hicieron, por agregando una construcción "goto" en 5.2 (que no se había lanzado cuando se escribió esta respuesta). Ver [esta respuesta de 2012] (https://stackoverflow.com/a/12929685/34799), después de que se lanzara 5.2.0. –

+0

Derecha - porque se reconoce que 'goto' es una construcción de programación decente. (fin del sarcasmo) Ah bien. –

14

La primera parte se responde en el FAQ como slain señalado.

En cuanto a una solución, puede envolver el cuerpo del bucle en una función y return al principio de eso, p. Ej.

-- 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 

O si desea la funcionalidad break y continue, tienen la función local de realizar la prueba, por ejemplo,

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 
+9

Por favor, no. Usted crea un entorno de cierre en cada iteración y esto es ENORME pérdida de memoria y ciclos de GC. –

+2

@ OlegV.Volkov: optimización prematura ... – finnw

+2

revisa 'collectgarbage (" count ")' incluso después de tus 100 intentos simples y luego hablaremos. Tal optimización "prematura" salvó un proyecto de carga alta de reiniciar cada minuto la semana pasada. –

3

De nuevo con la inversora, simplemente puede utilizar el siguiente código:

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

En Lua 5.2 la mejor solución es utilizar Goto:

-- prints odd numbers in [|1,10|] 
for i=1,10 do 
    if i % 2 == 0 then goto continue end 
    print(i) 
    ::continue:: 
end 

Esto se apoya en LuaJIT desde la versión 2.0.1

+27

Espero que incluyan un 'continuar' real un día. El reemplazo 'goto' no se ve muy bien y necesita más líneas. Además, ¿no crearía problemas si tuviera más de un ciclo haciendo esto en una función, ambos con ':: continue ::'? Crear un nombre por ciclo no suena como algo digno de hacer. –

33

Puede envolver el cuerpo del bucle en repeat until true adicional y luego usar do break end dentro para efecto de continuar. Naturalmente, también deberá configurar indicadores adicionales si también desea break fuera de loop.

Esto repetirá 5 veces, imprimiendo 1, 2 y 3 cada vez.

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 

Esta construcción aún se traduce en un opcode literal JMP en Lua código de bytes!

$ 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

Esta respuesta es agradable, pero aún requiere 3 líneas en lugar de solo una. (Si "continuar" fue soportado correctamente). Sin embargo, es un poco más bonito y seguro que una etiqueta goto, ya que para ese nombre, es posible que haya que evitar los choques para los bucles anidados. –

+3

, sin embargo, evita el problema "real" con goto en que no tiene que inventar un nuevo identificador/etiqueta para cada psuedo-continue y que es menos propenso a errores a medida que el código se modifica con el tiempo. * Estoy de acuerdo en que continuar sería útil *, pero esta IMO es la siguiente mejor opción (y realmente requiere dos líneas para la repetición/hasta vs. una más formal "continuar;" ... y aun así, si usted estaba tan preocupado con recuentos de líneas, siempre puede escribir "do repeat" y "until true end", por ejemplo: https://gist.github.com/wilson0x4d/f8410719033d1e0ef771) –

2

Podemos lograrlo como abajo, se saltará números pares

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 
1

Nos encontramos con este escenario muchas veces y simplemente usamos una bandera para simular continuar Tratamos de evitar el uso de declaraciones goto también.

Ejemplo: El código intenta imprimir los números del 1 al 10 excepto 3. Además, también imprime "inicio de bucle", final de bucle "," si inicio "y" si finaliza "para simular otras declaraciones que existen en el código y declaraciones anidadas.

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 

se logra encerrando todas las sentencias restantes hasta que el alcance final del bucle con un indicador de prueba.

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 

no estoy diciendo que este es el mejor enfoque pero nos funciona perfectamente.

Cuestiones relacionadas