Antworten:
In Lua 5.2 ist die beste Problemumgehung, goto zu verwenden:
-- prints odd numbers in [|1,10|]
for i=1,10 do
if i % 2 == 0 then goto continue end
print(i)
::continue::
end
Dies wird in LuaJIT seit Version 2.0.1 unterstützt
continue
einen Tag enthalten. Der goto
Ersatz sieht nicht sehr gut aus und benötigt mehr Zeilen. Würde das nicht auch Probleme verursachen, wenn Sie mehr als eine Schleife in einer Funktion hätten, beide mit ::continue::
? Einen Namen pro Schleife zu erfinden, klingt nicht nach einer anständigen Sache.
Die Art und Weise, wie die Sprache den lexikalischen Bereich verwaltet, führt zu Problemen beim Einbeziehen von goto
und continue
. Beispielsweise,
local a=0
repeat
if f() then
a=1 --change outer a
end
local a=f() -- inner a
until a==0 -- test inner a
Die Deklaration local a
innerhalb des Schleifenkörpers maskiert die genannte äußere Variable a
, und der Gültigkeitsbereich dieses lokalen Bereichs erstreckt sich über die Bedingung der until
Anweisung, sodass die Bedingung die innerste testet a
.
Wenn continue
vorhanden, müsste es semantisch eingeschränkt werden, um erst gültig zu sein, nachdem alle in der Bedingung verwendeten Variablen in den Geltungsbereich gekommen sind. Dies ist eine schwierige Bedingung, die dem Benutzer dokumentiert und im Compiler durchgesetzt werden kann. Verschiedene Vorschläge zu diesem Thema wurden diskutiert, einschließlich der einfachen Antwort, continue
den repeat ... until
Stil der Schleife nicht zuzulassen. Bisher hatte keiner einen ausreichend überzeugenden Anwendungsfall, um sie in die Sprache aufzunehmen.
Die Umgehung besteht im Allgemeinen darin, die Bedingung umzukehren, die dazu führen würde, dass a continue
ausgeführt wird, und den Rest des Schleifenkörpers unter dieser Bedingung zu sammeln. Also die folgende Schleife
-- 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
könnte geschrieben werden
-- 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 ist klar genug und normalerweise keine Belastung, es sei denn, Sie haben eine Reihe von aufwändigen Keulungen, die den Schleifenbetrieb steuern.
until...
.
goto
Lua 5.2 wurde in der Lua-Community viel darüber diskutiert . Hat natürlich goto
das gleiche Problem. Sie entschieden schließlich, dass unabhängig von den Laufzeit- und / oder Codegenerierungskosten zum Schutz die Vorteile einer Flexibilität goto
, die sowohl zur Emulation continue
als auch auf mehreren Ebenen verwendet werden kann, wert sind break
. Sie müssten die Lua-Listenarchive nach den relevanten Threads durchsuchen , um die Details zu erhalten. Da sie eingeführt haben goto
, war es offensichtlich nicht unüberwindbar.
local
ist eine reine Compiler-Direktive - es spielt keine Rolle, zwischen welchen Laufzeitinstruktionen local
und variabler Verwendung sie liegen - Sie müssen im Compiler nichts ändern, um das gleiche Gültigkeitsbereichsverhalten beizubehalten. Ja, dies ist möglicherweise nicht so offensichtlich und erfordert zusätzliche Dokumentation. Um es jedoch noch einmal zu wiederholen, sind NULL-Änderungen im Compiler erforderlich. repeat do break end until true
Beispiel in meiner Antwort generiert bereits genau den gleichen Bytecode, mit dem der Compiler fortfahren würde. Der einzige Unterschied besteht darin, dass continue
Sie für die Verwendung keine hässliche zusätzliche Syntax benötigen würden.
do{int i=0;}while (i == 0);
fehlgeschlagen oder in C ++: do int i=0;while (i==0);
schlägt ebenfalls fehl ("wurde in diesem Bereich nicht deklariert"). Leider zu spät, um das jetzt in Lua zu ändern.
Sie können den Schleifenkörper zusätzlich einwickeln repeat until true
und dann do break end
innen verwenden, um fortzufahren. Natürlich müssen Sie zusätzliche Flags einrichten, wenn Sie auch wirklich break
außerhalb der Schleife sein möchten.
Dies wird 5 Mal wiederholt und jedes Mal 1, 2 und 3 gedruckt.
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
Diese Konstruktion übersetzt sogar einen wörtlichen Opcode JMP
in Lua-Bytecode!
$ 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
auf SO! Habe eine wohlverdiente Gegenstimme :)
Direkt vom Designer von Lua selbst :
Unser Hauptanliegen bei "Weiter" ist, dass es mehrere andere Kontrollstrukturen gibt, die (unserer Ansicht nach) mehr oder weniger so wichtig sind wie "Weiter" und sie möglicherweise sogar ersetzen. (ZB brechen Sie mit Labels [wie in Java] oder sogar mit einem allgemeineren Goto.) "Continue" scheint nicht spezieller zu sein als andere Kontrollstrukturmechanismen, außer dass es in mehr Sprachen vorhanden ist. (Perl hat tatsächlich zwei "continue" -Anweisungen, "next" und "redo". Beide sind nützlich.)
continue
in Lua zu stecken, sorry."
Der erste Teil ist in der antwortete FAQ als getötet aufgezeigt.
Als Problemumgehung können Sie den Körper der Schleife in eine Funktion einbinden und return
frühzeitig daraus, z
-- 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
Wenn Sie beides break
und continue
Funktionalität wünschen , lassen Sie die lokale Funktion den Test durchführen, z
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")
auch nach Ihren einfachen 100 Versuchen, und dann werden wir reden. Eine solche "vorzeitige" Optimierung hat letzte Woche ein Hochlastprojekt vor dem Neustart jede Minute bewahrt.
Ich habe Lua noch nie benutzt, aber ich habe es gegoogelt und mir Folgendes ausgedacht:
Überprüfen Sie Frage 1.26 .
Dies ist eine häufige Beschwerde. Die Lua-Autoren waren der Ansicht, dass das Fortfahren nur einer von mehreren möglichen neuen Kontrollflussmechanismen ist (die Tatsache, dass es nicht mit den Bereichsregeln von Wiederholung / Bis arbeiten kann, war ein sekundärer Faktor).
In Lua 5.2 gibt es eine goto-Anweisung, mit der derselbe Job problemlos ausgeführt werden kann.
Wir sind diesem Szenario oft begegnet und verwenden einfach ein Flag, um die Fortsetzung zu simulieren. Wir versuchen, die Verwendung von goto-Anweisungen ebenfalls zu vermeiden.
Beispiel: Der Code beabsichtigt, die Anweisungen von i = 1 bis i = 10 mit Ausnahme von i = 3 zu drucken. Außerdem werden "Schleifenstart", "Schleifenende", "Wenn Start" und "Wenn Ende" gedruckt, um andere verschachtelte Anweisungen zu simulieren, die in Ihrem Code vorhanden sind.
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
wird erreicht, indem alle verbleibenden Anweisungen bis zum Ende der Schleife mit einem Testflag versehen werden.
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
Ich sage nicht, dass dies der beste Ansatz ist, aber er funktioniert perfekt für uns.
Auch beim Invertieren können Sie einfach den folgenden Code verwenden:
for k,v in pairs(t) do
if not isstring(k) then
-- do something to t[k] when k is not a string
end
Lua ist eine leichte Skriptsprache, die so klein wie möglich sein soll. Beispielsweise sind viele unäre Operationen wie das Inkrementieren vor / nach dem Inkrementieren nicht verfügbar
Anstatt fortzufahren, können Sie goto like verwenden
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
Weil es unnötig ist¹. Es gibt sehr wenige Situationen, in denen ein Entwickler es brauchen würde.
A) Wenn Sie eine sehr einfache Schleife haben, z. B. einen 1- oder 2-Liner, können Sie die Schleifenbedingung einfach umdrehen und sie ist immer noch gut lesbar.
B) Wenn Sie einfachen prozeduralen Code schreiben (auch bekannt als wie wir Code im letzten Jahrhundert geschrieben haben), sollten Sie auch strukturierte Programmierung anwenden (auch bekannt als wie wir besseren Code im letzten Jahrhundert geschrieben haben).
C) Wenn Sie objektorientierten Code schreiben, sollte Ihr Schleifenkörper aus nicht mehr als einem oder zwei Methodenaufrufen bestehen, es sei denn, er kann in einem Ein- oder Zweizeiler ausgedrückt werden (in diesem Fall siehe A).
D) Wenn Sie Funktionscode schreiben, geben Sie einfach einen einfachen Tail-Call für die nächste Iteration zurück.
Der einzige Fall, in dem Sie ein continue
Schlüsselwort verwenden möchten, ist, wenn Sie Lua wie Python codieren möchten, was es einfach nicht ist.²
Sofern A) nicht zutrifft, sollten Sie in diesem Fall keine Problemumgehungen vornehmen. Sie sollten eine strukturierte, objektorientierte oder funktionale Programmierung durchführen. Das sind die Paradigmen, für die Lua gebaut wurde. Sie würden also gegen die Sprache kämpfen, wenn Sie alles daran setzen, ihre Muster zu vermeiden.³
Einige Klarstellungen:
¹ Lua ist eine sehr minimalistische Sprache. Es wird versucht, so wenige Funktionen wie möglich zu haben, und eine continue
Aussage ist in diesem Sinne kein wesentliches Merkmal.
Ich denke, diese Philosophie des Minimalismus wird von Roberto Ierusalimschy in diesem Interview von 2019 gut festgehalten :
füge das und das und das hinzu, lösche das und am Ende verstehen wir, dass die endgültige Schlussfolgerung die meisten Menschen nicht zufriedenstellen wird und wir werden nicht alle Optionen setzen, die jeder will, also setzen wir nichts. Am Ende ist der strikte Modus ein vernünftiger Kompromiss.
² Es scheint eine große Anzahl von Programmierern zu geben, die aus anderen Sprachen nach Lua kommen, weil jedes Programm, für das sie ein Skript erstellen, es zufällig verwendet, und viele von ihnen scheinen nichts anderes als ihre Sprache schreiben zu wollen Auswahl, die zu vielen Fragen wie "Warum hat Lua keine X-Funktion?" führt.
Matz hat kürzlich in einem Interview eine ähnliche Situation mit Ruby beschrieben :
Die beliebteste Frage lautet: "Ich komme aus der Language X-Community. Können Sie Ruby keine Funktion aus der Sprache X vorstellen?" Oder so ähnlich. Und meine übliche Antwort auf diese Anfragen lautet… "Nein, das würde ich nicht tun", weil wir unterschiedliche Sprachdesign- und Sprachentwicklungsrichtlinien haben.
³ Es gibt einige Möglichkeiten, sich darum zu kümmern. Einige Benutzer haben die Verwendung vorgeschlagen goto
, was in den meisten Fällen eine gute Annäherung ist, aber sehr schnell sehr hässlich wird und vollständig mit verschachtelten Schleifen bricht. Wenn Sie goto
s verwenden, besteht auch die Gefahr, dass Sie eine Kopie von SICP erhalten, wenn Sie Ihren Code jemand anderem zeigen.
continue
Möglicherweise ist dies eine praktische Funktion, die jedoch nicht erforderlich ist . Viele Leute benutzen Lua ganz gut ohne es, also gibt es wirklich keinen Grund dafür, dass es etwas anderes als eine nette Funktion ist, die für keine Programmiersprache wesentlich ist.
goto
Erklärung, die zur Implementierung von continue verwendet werden kann. Siehe die Antworten unten.