Відповіді:
У Луа 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
continue
один день. goto
Заміна не виглядає дуже красиво і потребує в більшій кількості ліній. Крім того, чи не створить це проблеми, якби у вас було більше одного циклу, який виконує це в одній функції, обидва з ::continue::
? Створення імені за циклом не здається гідною справою.
Спосіб управління мовою лексичної сфери створює проблеми із включенням обох 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
стилем циклу. Поки жоден не мав достатньо переконливого випадку використання, щоб включити їх до мови.
Загальна робота полягає в тому, щоб перевернути умову, яка призведе 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
Це достатньо зрозуміло і зазвичай не є тягарем, якщо у вас є низка складних каналів, які контролюють роботу циклу.
until...
.
goto
у Lua 5.2. Природно, goto
має те саме питання. Зрештою, вони вирішили, що незалежно від витрат на виконання та / або створення коду, для захисту від цього варто скористатися перевагами, що мають гнучкість, goto
яку можна використовувати для емуляції як на continue
багаторівневому рівні break
. Вам потрібно буде шукати в архівах списків Lua для відповідних тем, щоб отримати детальну інформацію. Оскільки вони все-таки представили goto
, це, очевидно, було непереборним.
local
- це директива, що стосується лише компілятора - не важливо, між якими порушеннями виконання local
та перемінним використанням є порушення - вам не потрібно нічого змінювати в компіляторі, щоб підтримувати таку ж поведінку в масштабі. Так, це може бути не так очевидно і потребує додаткової документації, але, щоб ще раз зазначити, для зміни компілятора потрібні зміни ZERO. repeat do break end until true
Приклад в моїй відповіді вже генерує абсолютно той самий байт-код, який би продовжував компілятор, лише відмінність полягає в тому, що з continue
вами для його використання не знадобиться потворний зайвий синтаксис.
do{int i=0;}while (i == 0);
fails або у C ++: do int i=0;while (i==0);
також fails ("не було оголошено в цій області"). На жаль, занадто пізно, щоб змінити це в Луа, на жаль.
Ви можете загортати корпус петлі додатково, 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
у байт-коді Луа!
$ 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
вихід на SO! Майте заслужений результат :)
Прямо від самого дизайнера Луа :
Наша основна проблема "продовжувати" полягає в тому, що існує кілька інших структур управління, які (на наш погляд) мають більш-менш важливе значення, як "продовжувати", і навіть можуть замінити його. (Наприклад, перерва з мітками [як у Java] або навіть більш загальним goto.) "Продовження" не здається більш особливим, ніж інші механізми структури управління, за винятком того, що він присутній у більшості мов. (Perl насправді має два заяви "продовжити", "наступний" і "повторно". Обидва корисні.)
continue
у Луа, вибач".
Перша частина відповідає в 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
collectgarbage("count")
навіть після ваших простих 100 спроб, і тоді ми поговоримо. Така "передчасна" оптимізація врятувала один проект з високим завантаженням щодня перезавантажуватись щохвилини.
Я ніколи раніше не використовував Lua, але я погуглив його і придумав таке:
Перевірте питання 1.26 .
Це звичайна скарга. Автори Луа вважали, що продовження є лише одним із низки можливих нових механізмів управління потоком (той факт, що він не може працювати з правилами застосування повтору / поки не був вторинним фактором).
У Lua 5.2 є goto-твердження, яке можна легко використовувати для виконання тієї ж роботи.
Ми стикалися з цим сценарієм багато разів, і ми просто використовуємо прапор, щоб імітувати продовження. Ми намагаємось також уникати використання goto-операторів.
Приклад: Код має намір надрукувати твердження від i = 1 до i = 10, за винятком i = 3. Крім того, він також друкує "цикл початку", цикл циклу "," якщо запуск "і" якщо кінець ", щоб імітувати інші вкладені заяви, які існують у вашому коді.
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
досягається додаванням всіх решти операторів до кінця області циклу тестовим прапором.
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
Я не кажу, що це найкращий підхід, але він прекрасно працює для нас.
Lua - це легка сценарна мова, яка хоче якомога менше. Наприклад, багато одинарних операцій, таких як збільшення до / після збільшення, недоступні
Замість того, щоб продовжувати, ви можете використовувати 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
Знову з інвертуванням ви можете просто використовувати наступний код:
for k,v in pairs(t) do
if not isstring(k) then
-- do something to t[k] when k is not a string
end
Тому що це зайве¹. Дуже мало ситуацій, коли потрібен був би девіз.
A) Якщо у вас дуже простий цикл, скажімо, 1- або 2-вкладиш, ви можете просто повернути стан циклу, і він ще досить читабельний.
B) Коли ви пишете простий процедурний код (ака. Як ми писали код у минулому столітті), ви також повинні застосовувати структуроване програмування (ака. Як ми писали кращий код у минулому столітті)
C) Якщо ви пишете об'єктно-орієнтований код, тіло циклу має складатися не більше ніж з одного або двох викликів методів, якщо його не можна виразити в одно- або двовкладинці (у цьому випадку див. A)
Г) Якщо ви пишете функціональний код, просто поверніть звичайний дзвінок у відповідь на наступну ітерацію.
Єдиний випадок, коли ви хочете скористатися continue
ключовим словом, це якщо ви хочете кодувати Lua як би python, а це просто немає.
Якщо не застосовується A), то в цьому випадку немає потреби в жодних обхідних шляхах, вам слід робити структуроване, об'єктно-орієнтоване або функціональне програмування. Це парадигми, для яких була побудована Луа, тож ви б'єтесь проти мови, якщо ви вийдете зі свого шляху, щоб уникнути їхніх зразків.³
Деякі роз’яснення:
¹ Lua - дуже мінімалістична мова. Намагається мати якомога менше функцій, як це може відійти, і continue
твердження не є істотною особливістю в цьому сенсі.
Я думаю, що ця філософія мінімалізму добре захоплена Роберто Ієрусалімщі в інтерв'ю 2019 року :
Додайте те і те, і це, викладіть це, і врешті-решт ми зрозуміли, що остаточний висновок не задовольнить більшість людей, і ми не будемо ставити всі варіанти, які всі бажають, тому ми нічого не ставимо. Зрештою, суворий режим - це розумний компроміс.
² Схоже, існує велика кількість програмістів, які приїжджають до Lua з інших мов, тому що, яку б програму вони не намагалися скриптувати, це трапляється використовувати її, і багато хто з них, схоже, не хочуть писати нічого, крім своєї мови вибір, який призводить до багатьох питань, таких як "Чому у Lua немає функції X?"
У недавньому інтерв'ю Мац описав подібну ситуацію з Рубі :
Найпопулярніше питання: "Я з мовної спільноти X; чи не можете ви ввести функцію від мови X до Ruby?", Або щось подібне. І моя звичайна відповідь на ці запити - "ні, я б цього не робив", тому що у нас різний дизайн мови та різна політика розвитку мови.
³ Є кілька способів зламати ваш шлях; деякі користувачі пропонують використовувати goto
, що в більшості випадків є досить хорошим, але стає дуже некрасивим і швидко розривається з вкладеними петлями. Використання goto
s також створює небезпеку того, що копія SICP буде кинута на вас, коли ви показуєте свій код комусь іншому.
continue
може бути зручною функцією, але це не робить її необхідною . Дуже багато людей використовують Lua просто без неї, тому насправді не існує випадку, якщо це була б окрім акуратної функції, неістотної для будь-якої мови програмування.
goto
заяву, яку можна використовувати для продовження. Відповіді дивіться нижче.