Управляющие конструкции и циклы в Lua
Перед чтением: Операторы — общие понятия оператора, операнда, приоритетов и типов операций без привязки к языку.
Сначала: Циклы в коде — общая идея повторений, виды циклов и типичные ошибки без привязки к синтаксису языка.
Управляющие конструкции и циклы в Lua
Парадигма короткого замыкания
Lua поддерживает три базовых логических оператора — and, or, not. Их поведение следует парадигме короткого замыкания (short-circuit evaluation) и тесно связано с концепцией "истинности" значений в условиях отсутствия строгой булевой типизации.
Ранее мы уже вкратце упомянули о том, что они заменяют собой некоторые символьные операторы, чем и упрощают язык, ведь мы словно пишем на английском языке.
Play ITЗагрузка интерактивного демо…
not
not — унарный оператор, возвращающий true, если операнд является nil или false, и false — во всех остальных случаях. Результат всегда имеет тип boolean.
not nil --> true
not false --> true
not 0 --> false (0 считается истинным!)
not "" --> false
Разбор:
- Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
and
and — бинарный оператор, возвращающий первый аргумент, если он ложный (nil или false), иначе — второй. Не приводит результат к булеву типу, а возвращает фактическое значение последнего вычисленного операнда.
true and 42 --> 42
false and 42 --> false
nil and "hello" --> nil
Разбор:
- Логика записывается словами
and/or/not, а не символами&&/||/!как в C-подобных языках. - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
or
or — возвращает первый истинный операнд или последний, если все ложны. Также не производит приведение типов.
nil or "default" --> "default"
false or 0 --> 0 (0 — истинно!)
"a" or "b" --> "a"
Разбор:
- Логика записывается словами
and/or/not, а не символами&&/||/!как в C-подобных языках. - Выражение
a or "default"— идиома: еслиaложно (nil/false), берётся значение справа. - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Truthy (истинные значения)
В Lua всё, кроме nil и false, считается истинным (truthy). Это включает числа (даже ноль), строки (даже пустые), таблицы, функции и т.д. Данная особенность позволяет использовать выражения как идиому для установки значения по умолчанию:
local value = input or "default"
Разбор:
- Логика записывается словами
and/or/not, а не символами&&/||/!как в C-подобных языках. - Выражение
a or "default"— идиома: еслиaложно (nil/false), берётся значение справа. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Таким образом, операторы and и or являются средствами управления потоком данных, что характерно для функциональных и скриптовых парадигм.
Условные конструкции
Lua предоставляет две формы условной конструкции: if-then-else и её компактный аналог через and/or, хотя последний используется с осторожностью из-за семантических различий.
Синтаксис if-then-else следующий:
if condition1 then
-- блок 1
elseif condition2 then
-- блок 2
else
-- блок 3
end
Разбор:
if … then … endвыбирает ветку по truthiness: ложными считаются толькоnilиfalse.- Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Условие (condition) интерпретируется как булево выражение согласно правилу truthiness.
Конструкция elseif не является макросом или сахаром — это часть синтаксиса, эквивалентная вложенному if, но более читаемая.
Блоки выполняются последовательно; после первого истинного условия остальные игнорируются.
Вложенность допускается без ограничений.
Пример:
if x > 0 then
print("positive")
elseif x < 0 then
print("negative")
else
print("zero")
end
Разбор:
if … then … endвыбирает ветку по truthiness: ложными считаются толькоnilиfalse.- Ключевые вызовы в фрагменте:
print(). - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Важно отметить, что в отличие от C-подобных языков, в Lua отсутствует тернарный оператор ?:. Однако его можно эмулировать через and/or, при соблюдении осторожности:
local result = (a > b) and x or y -- работает, если x не ложно
Разбор:
- Логика записывается словами
and/or/not, а не символами&&/||/!как в C-подобных языках. - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Однако если x == false или x == nil, выражение вернёт y, что может быть ошибкой. Поэтому рекомендуется использовать полноценный if.
Интерактивное демо — пошаговый цикл на примере JavaScript (
for,while). В Lua синтаксис другой, но порядок шагов тот же. Обобщённо: циклы в коде.
Play ITЗагрузка интерактивного демо…
Циклы
Теперь поговорим о циклах.
Lua поддерживает три типа циклов: while, repeat-until и for (с двумя формами).
while
Цикл while выполняет тело, пока условие истинно. Проверка происходит до каждой итерации. Если условие ложно изначально, тело не выполнится ни разу.
while condition do
-- тело цикла
end
Разбор:
whileпроверяет условие до входа в тело;repeat…until— хотя бы один проход, выход при истинномuntil.- Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
repeat-until
Цикл repeat-until представляет собой аналог do-while в других языках. Тело выполняется хотя бы один раз, проверка — в конце.
repeat
-- тело цикла
until condition -- выход при истине условия
Разбор:
whileпроверяет условие до входа в тело;repeat…until— хотя бы один проход, выход при истинномuntil.- Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Обратите внимание — until завершает цикл, когда условие становится истинным, что противоположно семантике while.
for
Цикл for бывает в двух формах - числовой и итерационный.
Числовой for используется для перебора диапазона целых чисел.
for var = start, stop, step do
-- тело
end
Разбор:
- Цикл
forповторяет тело: в числовой форме перебирает диапазон, в generic — элементы через итератор. - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
step по умолчанию равен 1.
Все три параметра вычисляются единожды перед началом цикла.
Переменная var локальна по отношению к циклу (это важно!).
for i = 1, 10, 2 do
print(i) -- 1, 3, 5, 7, 9
end
Разбор:
- Цикл
forповторяет тело: в числовой форме перебирает диапазон, в generic — элементы через итератор. - Ключевые вызовы в фрагменте:
print(). - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Итерационный for (generic for) предназначен для обхода коллекций с помощью итераторов. Наиболее часто используется с таблицами.
for key, value in iterator, table, start_state do
-- тело
end
Разбор:
- Цикл
forповторяет тело: в числовой форме перебирает диапазон, в generic — элементы через итератор. - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
На практике применяются стандартные итераторы:
- pairs(tbl) — перебирает все элементы таблицы (в произвольном порядке).
- ipairs(tbl) — перебирает элементы с целочисленными ключами от 1 до первого nil.
for k, v in pairs(mytable) do
print(k, v)
end
Разбор:
ipairsобходит последовательную часть таблицы (индексы 1,2,3…);pairs— все пары ключ-значение.- Цикл
forповторяет тело: в числовой форме перебирает диапазон, в generic — элементы через итератор. - Ключевые вызовы в фрагменте:
pairs(),print(). - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Использование pairs и ipairs требует понимания различий: pairs негарантированный, а ipairs последовательный.
Механизм generic for основан на протоколе итераций — итератор — это функция, возвращающая на каждом шаге новые значения ключа/значения, пока не вернёт nil.
t = {10, 20, nil, 40, [5] = 50, name = "Alice"}
for i, v in ipairs(t) do
print(i, v) -- 1:10, 2:20 (остановка на nil)
end
for k, v in pairs(t) do
print(k, v) -- может напечатать 1,2,5,"name" в любом порядке
end
Разбор:
ipairsобходит последовательную часть таблицы (индексы 1,2,3…);pairs— все пары ключ-значение.- Цикл
forповторяет тело: в числовой форме перебирает диапазон, в generic — элементы через итератор. - Фигурные скобки
{}создают таблицу: можно задать поляkey = valueи/или элементы-массив с индексами с 1. - Ключевые вызовы в фрагменте —
ipairs(),pairs(),print(). - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
При модификации таблицы во время итерации поведение pairs и ipairs не определено. Lua не гарантирует безопасность итерации при изменении структуры.