W ciągu ostatnich kilku miesięcy dużo miałem do czynienia z Luą i bardzo lubię większość funkcji, ale wciąż czegoś mi brakuje:
- Dlaczego nie ma
continue
? - Jakie są obejścia tego problemu?
loops
lua
language-design
Dant
źródło
źródło
goto
oświadczenie, które można wykorzystać do wdrożenia kontynuacji. Zobacz odpowiedzi poniżej.Odpowiedzi:
W Lua 5.2 najlepszym obejściem jest użycie goto:
Jest to obsługiwane w LuaJIT od wersji 2.0.1
źródło
continue
jeden dzień.goto
Wymiana nie wygląda bardzo ładnie i potrzebuje więcej linii. Ponadto, czy nie spowodowałoby to problemów, gdybyś miał więcej niż jedną pętlę wykonującą to w jednej funkcji, obie z::continue::
? Tworzenie nazwy dla każdej pętli nie brzmi jak przyzwoita rzecz.Sposób, w jaki język zarządza zakresem leksykalnym, stwarza problemy z włączaniem zarówno
goto
icontinue
. Na przykład,Deklaracja
local a
wewnątrz ciała pętli maskuje zewnętrzną zmienną o nazwiea
, a zakres tego lokalnego rozciąga się na warunekuntil
instrukcji, więc warunek testuje najbardziej wewnętrznąa
.Gdyby
continue
istniał, musiałby być ograniczony semantycznie, aby był ważny tylko wtedy, gdy wszystkie zmienne użyte w warunku wejdą w zakres. Jest to trudny warunek do udokumentowania użytkownikowi i wprowadzenia go w kompilatorze. Omówiono różne propozycje dotyczące tego problemu, w tym prostą odpowiedź polegającą na blokowaniucontinue
za pomocąrepeat ... until
stylu pętli. Jak dotąd żaden nie miał wystarczająco przekonującego przypadku użycia, aby włączyć je do języka.Obejście polega na ogół na odwróceniu warunku, który spowodowałby
continue
wykonanie a, i zebraniu reszty treści pętli pod tym warunkiem. Tak więc następująca pętlamożna napisać
Jest to wystarczająco jasne i zwykle nie jest obciążeniem, chyba że masz serię wyszukanych eliminacji kontrolujących działanie pętli.
źródło
until...
.goto
do Lua 5.2. Oczywiściegoto
ma ten sam problem. Ostatecznie zdecydowali, że bez względu na koszty środowiska wykonawczego i / lub generowania kodu, które miałyby przed nimi chronić, warte są korzyści wynikające z posiadania elastycznego rozwiązania,goto
które może być używane do emulacji zarówno nacontinue
wielu poziomach, jak i na wielu poziomachbreak
. Aby uzyskać szczegółowe informacje , musisz przeszukać archiwa listy Lua pod kątem odpowiednich wątków. Odkąd wprowadziligoto
, oczywiście nie było to nie do pokonania.local
to dyrektywa tylko dla kompilatora - nie ma znaczenia, jakie instrukcje w czasie wykonywania znajdują się pomiędzylocal
i użycie zmiennych - nie musisz nic zmieniać w kompilatorze, aby zachować takie samo zachowanie zakresu. Tak, może to nie być takie oczywiste i wymagać dodatkowej dokumentacji, ale, aby powtórzyć, wymaga ZERO zmian w kompilatorze.repeat do break end until true
przykład w mojej odpowiedzi już generuje dokładnie ten sam kod bajtowy, który kompilator użyłby dalej, jedyną różnicą jest tocontinue
, że nie potrzebowałbyś brzydkiej dodatkowej składni, aby go użyć.do{int i=0;}while (i == 0);
fail lub w C ++:do int i=0;while (i==0);
również nie powiedzie się („nie został zadeklarowany w tym zakresie”). Niestety za późno, żeby to zmienić teraz w Lua.Możesz dodatkowo owinąć pętlę,
repeat until true
a następnie użyćdo break end
wewnątrz, aby uzyskać efekt kontynuacji. Oczywiście będziesz musiał ustawić dodatkowe flagi, jeśli chcesz również naprawdębreak
wyjść z pętli.Spowoduje to zapętlenie 5 razy, za każdym razem drukując 1, 2 i 3.
Ta konstrukcja przekłada się nawet na dosłowny jeden kod operacji
JMP
w kodzie bajtowym Lua!źródło
luac
wyjście na SO! Życzę wszystkim zasłużonych głosów :)Prosto od samego projektanta Lua :
źródło
continue
do Lua, przepraszam”.Pierwsza część odpowiedzi w FAQ jak zabitego zauważył.
Jeśli chodzi o obejście problemu, możesz zawinąć ciało pętli w funkcję i
return
wcześniej, npLub jeśli chcesz mieć obie
break
icontinue
funkcjonalność, poproś funkcję lokalną o wykonanie testu, npźródło
collectgarbage("count")
nawet po twoich prostych 100 próbach, a wtedy porozmawiamy. Taka „przedwczesna” optymalizacja uratowała w zeszłym tygodniu jeden projekt o dużym obciążeniu przed ponownym uruchamianiem co minutę.Nigdy wcześniej nie używałem Lua, ale wyszukałem go w Google i wymyśliłem to:
http://www.luafaq.org/
Sprawdź pytanie 1.26 .
źródło
Możemy to osiągnąć jak poniżej, pominie liczby parzyste
O / P:
źródło
Napotkaliśmy ten scenariusz wiele razy i po prostu używamy flagi do symulacji kontynuacji. Staramy się również unikać stosowania instrukcji goto.
Przykład: Kod zamierza wydrukować instrukcje od i = 1 do i = 10, z wyjątkiem i = 3. Ponadto wypisuje również „początek pętli”, „koniec pętli”, „jeśli początek” i „jeśli koniec”, aby zasymulować inne zagnieżdżone instrukcje istniejące w kodzie.
osiąga się poprzez zamknięcie wszystkich pozostałych instrukcji aż do końca zakresu pętli flagą test.
Nie mówię, że jest to najlepsze podejście, ale dla nas działa doskonale.
źródło
Lua to lekki język skryptowy, który powinien być jak najmniejszy. Na przykład wiele operacji jednoargumentowych, takich jak inkrementacja przed / po, nie jest dostępnych
Zamiast kontynuować, możesz użyć goto like
źródło
Ponownie z odwracaniem, możesz po prostu użyć następującego kodu:
źródło
Dlaczego nie ma kontynuacji?
Ponieważ jest to niepotrzebne¹. Jest bardzo niewiele sytuacji, w których deweloper by tego potrzebował.
A) Kiedy masz bardzo prostą pętlę, powiedzmy 1- lub 2-liniową, możesz po prostu odwrócić stan pętli i nadal jest bardzo czytelny.
B) Kiedy piszesz prosty kod proceduralny (czyli sposób, w jaki pisaliśmy kod w ubiegłym wieku), powinieneś również stosować programowanie strukturalne (czyli jak napisaliśmy lepszy kod w ubiegłym wieku)
C) Jeśli piszesz kod zorientowany obiektowo, treść pętli powinna składać się z nie więcej niż jednego lub dwóch wywołań metod, chyba że można to wyrazić w jedno- lub dwuwierszowym (w takim przypadku zobacz A)
D) Jeśli piszesz kod funkcjonalny, po prostu zwróć zwykłe wywołanie końcowe dla następnej iteracji.
Jedynym przypadkiem, w którym chciałbyś użyć
continue
słowa kluczowego, jest zakodowanie Lua tak, jak w Pythonie, którym po prostu nie jest .²Jakie są obejścia tego problemu?
O ile nie ma zastosowania A), w którym to przypadku nie ma potrzeby stosowania żadnych obejść, należy wykonywać programowanie strukturalne, obiektowe lub funkcjonalne. To są paradygmaty, do których stworzono Lua, więc walczyłbyś z językiem, gdybyś zrobił wszystko, co w twojej mocy, aby uniknąć ich wzorców³.
Kilka wyjaśnień:
¹ Lua to bardzo minimalistyczny język. Próbuje mieć tak mało funkcji, jak to tylko możliwe, a
continue
stwierdzenie nie jest w tym sensie istotną funkcją.Myślę, że ta filozofia minimalizmu została dobrze ujęta przez Roberto Ierusalimschy w wywiadzie z 2019 roku :
² Wydaje się, że duża liczba programistów przyjeżdża do Lua z innych języków, ponieważ każdy program, dla którego próbują napisać, używa go, a wielu z nich nie chce pisać niczego innego niż język wybór, co prowadzi do wielu pytań, takich jak „Dlaczego Lua nie ma funkcji X?”
Matz opisał podobną sytuację z Ruby w niedawnym wywiadzie :
³ Jest kilka sposobów obejścia tego problemu; niektórzy użytkownicy sugerowali użycie
goto
, co w większości przypadków jest wystarczająco dobrym przybliżeniem, ale bardzo szybko staje się bardzo brzydkie i całkowicie zrywa z zagnieżdżonymi pętlami. Używaniegoto
s naraża Cię również na ryzyko rzucenia kopii SICP za każdym razem, gdy pokażesz komuś swój kod.źródło
continue
może być wygodną funkcją, ale to nie oznacza, że jest to konieczne . Wiele osób używa Lua bez niego, więc naprawdę nie ma powodu, aby był to coś innego niż zgrabna funkcja, która nie jest niezbędna w żadnym języku programowania.