Відповіді:
У Луа 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, що в більшості випадків є досить хорошим, але стає дуже некрасивим і швидко розривається з вкладеними петлями. Використання gotos також створює небезпеку того, що копія SICP буде кинута на вас, коли ви показуєте свій код комусь іншому.
continueможе бути зручною функцією, але це не робить її необхідною . Дуже багато людей використовують Lua просто без неї, тому насправді не існує випадку, якщо це була б окрім акуратної функції, неістотної для будь-якої мови програмування.
gotoзаяву, яку можна використовувати для продовження. Відповіді дивіться нижче.