Risposte:
In Lua 5.2 la soluzione migliore è usare goto:
-- prints odd numbers in [|1,10|]
for i=1,10 do
if i % 2 == 0 then goto continue end
print(i)
::continue::
end
Questo è supportato in LuaJIT dalla versione 2.0.1
continue
giorno reale . La goto
sostituzione non sembra molto bella e necessita di più linee. Inoltre, ciò non creerebbe problemi se avessi più di un ciclo che lo fa in una funzione, entrambi con ::continue::
? Creare un nome per loop non sembra una cosa decente da fare.
Il modo in cui la lingua gestisce l'ambito lessicale crea problemi con l'inclusione di entrambi goto
e continue
. Per esempio,
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 dichiarazione di local a
all'interno del corpo del loop maschera la variabile esterna denominata a
e l'ambito di quel locale si estende attraverso la condizione until
dell'istruzione in modo che la condizione stia testando la più interna a
.
Se continue
esistesse, dovrebbe essere limitato semanticamente per essere valido solo dopo che tutte le variabili utilizzate nella condizione sono entrate nell'ambito. Questa è una condizione difficile da documentare per l'utente e far valere nel compilatore. Sono state discusse varie proposte su questo tema, inclusa la semplice risposta di non consentire continue
con lo repeat ... until
stile del loop. Finora, nessuno ha avuto un caso d'uso sufficientemente convincente per includerli nella lingua.
Il lavoro intorno è generalmente di invertire la condizione che causerebbe l' continue
esecuzione di un, e raccogliere il resto del corpo del loop in quella condizione. Quindi, il seguente ciclo
-- 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
potrebbe essere scritto
-- 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
È abbastanza chiaro, e di solito non è un onere a meno che tu non abbia una serie di abbattimenti elaborati che controllano il funzionamento del loop.
until...
.
goto
Lua 5.2. Naturalmente, goto
ha lo stesso problema. Alla fine decisero che qualunque fosse il costo di runtime e / o di generazione del codice da proteggere da esso valeva i vantaggi di avere un flessibile goto
che può essere usato per emulare sia a continue
più livelli break
. Dovresti cercare negli archivi dell'elenco Lua i thread pertinenti per ottenere i dettagli. Dal momento che hanno introdotto goto
, ovviamente non era insormontabile.
local
è una direttiva solo per il compilatore - non importa quali sono le istruzioni di runtime tra local
e l'uso variabile - non è necessario modificare nulla nel compilatore per mantenere lo stesso comportamento di scoping. Sì, questo potrebbe non essere così ovvio e richiedere una documentazione aggiuntiva, ma, per ribadirlo, richiede modifiche ZERO nel compilatore. repeat do break end until true
esempio nella mia risposta genera già esattamente lo stesso bytecode con cui il compilatore continuerebbe, l'unica differenza è che con continue
te non avresti bisogno di una brutta sintassi extra per usarlo.
do{int i=0;}while (i == 0);
fail o anche in C ++: do int i=0;while (i==0);
fail ("non è stato dichiarato in questo ambito"). Purtroppo è troppo tardi per cambiarlo adesso a Lua.
Puoi avvolgere il corpo del loop in più repeat until true
e quindi utilizzarlo do break end
all'interno per effetto di continue. Ovviamente, dovrai impostare ulteriori flag se intendi anche break
uscire davvero dal circuito.
Questo ripeterà 5 volte, stampando 1, 2 e 3 ogni volta.
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
Questa costruzione si traduce persino in un codice operativo letterale JMP
in bytecode 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
luac
output su SO! Avere un meritato voto :)
Direttamente dal designer di Lua stesso :
La nostra principale preoccupazione per "continuare" è che ci sono molte altre strutture di controllo che (a nostro avviso) sono più o meno importanti quanto "continuare" e possono persino sostituirlo. (Ad esempio, rompere con etichette [come in Java] o anche un goto più generico.) "Continue" non sembra più speciale di altri meccanismi di struttura di controllo, tranne che è presente in più lingue. (In realtà Perl ha due istruzioni "continue", "next" e "redo". Entrambe sono utili.)
continue
in Lua, scusa."
La prima parte è risolta in senso FAQ come ucciso fuori appuntito.
Per quanto riguarda una soluzione alternativa, puoi avvolgere il corpo del loop in una funzione e return
presto da quello, ad es
-- 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
Oppure, se si desidera sia la funzionalità break
che la continue
funzionalità, fare eseguire il test dalla funzione locale, ad es
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
collectgarbage("count")
anche dopo i tuoi semplici 100 tentativi e poi parleremo. Tale ottimizzazione "prematura" ha salvato un progetto ad alto carico dal riavvio ogni minuto la scorsa settimana.
Non ho mai usato Lua prima, ma l'ho cercato su Google e ho pensato a questo:
Controlla la domanda 1.26 .
Questa è una lamentela comune. Gli autori di Lua hanno ritenuto che continuare fosse solo uno dei numerosi possibili nuovi meccanismi di flusso di controllo (il fatto che non potesse funzionare con le regole dell'ambito di ripetizione / fino a quando non fosse un fattore secondario).
In Lua 5.2, c'è un'istruzione goto che può essere facilmente usata per fare lo stesso lavoro.
Abbiamo incontrato questo scenario molte volte e abbiamo semplicemente usato una bandiera per simulare continua. Cerchiamo di evitare anche l'uso di dichiarazioni goto.
Esempio: il codice intende stampare le istruzioni da i = 1 a i = 10 tranne i = 3. Inoltre stampa anche "loop start", loop end "," if start "e" if end "per simulare altre istruzioni nidificate presenti nel codice.
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
si ottiene racchiudendo tutte le restanti istruzioni fino alla fine del ciclo con un flag di prova.
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
Non sto dicendo che questo è l'approccio migliore ma funziona perfettamente per noi.
Lua è un linguaggio di scripting leggero che vuole essere il più piccolo possibile. Ad esempio, molte operazioni unarie come l'incremento pre / post non sono disponibili
Invece di continuare, puoi usare goto like
arr = {1,2,3,45,6,7,8}
for key,val in ipairs(arr) do
if val > 6 then
goto skip_to_next
end
# perform some calculation
::skip_to_next::
end
Ancora una volta con l'inversione, potresti semplicemente usare il seguente codice:
for k,v in pairs(t) do
if not isstring(k) then
-- do something to t[k] when k is not a string
end
Perché non è necessario¹. Ci sono pochissime situazioni in cui uno sviluppatore ne avrebbe bisogno.
A) Quando hai un loop molto semplice, dì un 1 o 2-liner, allora puoi semplicemente cambiare la condizione del loop ed è ancora molto leggibile.
B) Quando scrivi un semplice codice procedurale (ovvero come abbiamo scritto codice nell'ultimo secolo), dovresti anche applicare la programmazione strutturata (ovvero come abbiamo scritto un codice migliore nell'ultimo secolo)
C) Se stai scrivendo un codice orientato agli oggetti, il tuo corpo del loop dovrebbe consistere in non più di una o due chiamate di metodo a meno che non possa essere espresso in una o due righe (nel qual caso, vedi A)
D) Se stai scrivendo un codice funzionale, restituisci semplicemente una semplice chiamata di coda per la successiva iterazione.
L'unico caso in cui vorresti usare una continue
parola chiave è se vuoi codificare Lua come se fosse Python, cosa che non è.
A meno che non si applichi A), nel qual caso non è necessaria alcuna soluzione alternativa, è necessario eseguire una programmazione strutturata, orientata agli oggetti o funzionale. Questi sono i paradigmi per cui è stata costruita Lua, quindi combatteresti contro il linguaggio se fai del tuo meglio per evitare i loro schemi.
Alcuni chiarimenti:
¹ Lua è un linguaggio molto minimalista. Cerca di avere il minor numero possibile di funzioni e continue
un'affermazione non è una caratteristica essenziale in tal senso.
Penso che questa filosofia del minimalismo sia catturata bene da Roberto Ierusalimschy in questa intervista del 2019 :
aggiungi questo e quello e quello, mettilo fuori, e alla fine capiamo che la conclusione finale non soddisferà la maggior parte delle persone e non metteremo tutte le opzioni che tutti vogliono, quindi non mettiamo nulla. Alla fine, la modalità rigorosa è un ragionevole compromesso.
² Sembra che ci sia una grande quantità di programmatori che arrivano a Lua da altre lingue perché qualsiasi programma stanno cercando di scrivere per usarlo, e molti di loro vogliono non sembrano voler scrivere altro che il loro linguaggio di scelta, che porta a molte domande come "Perché Lua non ha la funzione X?"
Matz ha descritto una situazione simile con Ruby in una recente intervista :
La domanda più popolare è: "Vengo dalla comunità della lingua X; non puoi introdurre una funzionalità dalla lingua X a Ruby?" O qualcosa del genere. E la mia solita risposta a queste richieste è ... "no, non lo farei", perché abbiamo un design del linguaggio diverso e politiche di sviluppo del linguaggio diverse.
³ Ci sono alcuni modi per aggirare il problema; alcuni utenti hanno suggerito di utilizzare goto
, il che è una buona approssimazione nella maggior parte dei casi, ma diventa molto brutto molto rapidamente e si rompe completamente con loop nidificati. L'uso di goto
s ti mette anche in pericolo di avere una copia di SICP lanciata contro di te ogni volta che mostri il tuo codice a chiunque altro.
continue
potrebbe essere una funzione utile, ma ciò non lo rende necessario . Molte persone usano Lua bene senza di essa, quindi non c'è davvero nessun motivo per essere nient'altro che una caratteristica ordinata che non è essenziale per nessun linguaggio di programmazione.
goto
dichiarazione che può essere utilizzata per implementare continua. Vedi le risposte sotto.