Условный оператор и логика в Lua: специфика и реализация

Опубликовано:

Хотя Lua — лаконичный язык, логика в нем имеет ряд "подводных камней", которые необходимо учитывать выходцам из других языков программирования. Кратко:

Синтаксис условного оператора в Lua

В Lua операторными скобками условного оператора (маркерами границ блока) являются ключевые слова then и end. Однако в отличие от Паскаля если условный оператор имеет несколько ветвей, end добавляется только после последней. В Lua имеется настоящее множественное ветвление. Однако в отличие от Python ключевым словом для дополнительных веток является elseif.

local score = io.read('n')

if score >= 90 then
    print('Отлично')
elseif score >= 75 then
    print('Хорошо')
elseif score >= 50 then
    print('Сойдет')
    print('Но надо лучше')
else
    print('Неудовлетворительно')
end

Реализация конструкции выбора с помощью таблиц

В Lua нет оператора switch как в C++, или match как в Python. Однако подобие такого переключателя создают с помощью таблиц (хеш-карт), в которых ключами являются условия, а значениями — функции. Этот подход называют табличным ветвлением (table-driven approach). Он работает быстрее, чем длинная цепочка if-elseif, и код получается чище.

local actions = {
    move = function() print('Персонаж идет вперед...') end,
    jump = function() print('Персонаж подпрыгнул!') end,
    attack = function() print('Персонаж атакует мечом!') end
}
actions['jump']()

-- Если нужна проверка
local action = actions[io.read()]
if action then
    action()
else
    print('Неизвестная команда')
end
Персонаж подпрыгнул!
attack
Персонаж атакует мечом!

Когда требуется просто вернуть значение в зависимости от ключа, а не выполнять код, функции не нужны:

local colors = {
    red = '#FF0000',
    green = '#00FF00',
    blue = '#0000FF'
}

local input = io.read()
local hex = colors[input] or '#FFFFFF' 

print(hex)
green
#00FF00

Особенности логических выражений и эмуляция тернарного оператора

В Lua логические выражения работают по принципу короткозамкнутых вычислений (short-circuit evaluation), то есть если результат определен, дальнейшие вычисления не производятся. Кроме этого, логические операторы and и or, как и в Python, возвращают не просто true/false, а сам объект (операнд), на котором завершилась проверка условия. Поэтому в предыдущем примере мы могли использовать выражение colors[input] or '#FFFFFF'.

Обратите внимание, оператор not всегда возвращает boolean-значение (true или false).

Классический тернарный оператор условие ? true_выражение : false_выражение эмулируется связкой (общепринятой идиомой):

условие and true_выражение or false_выражение

local balance = io.read('n')
local status = (balance > 0) and 'В плюсе' or 'В минусе'
print(status)
6
В плюсе
-4
В минусе

В примере когда условие истинно, то выражение true and 'В плюсе' возвращает второй операнд — в данном случае строку. Получается выражение 'В плюсе' or 'В минусе'. Поскольку первый операнд ложью не является, он и возвращается.

Если условие ложно, выражение false and 'В плюсе' возвращает первый операнд. Получается false or 'В минусе'. В таких случаях оператор or возвращает второй операнд.

Однако такая эмуляция тернарного оператора не подходит для случаев, когда код, который должен срабатывать при истинности условия, сам по себе возвращает false или nil. В этом случае тоже получается false or .... Пример такой логической ошибки:

local isAdmin = true

-- Хотим для админа вернуть false
local showAds = isAdmin and false or true

print(showAds) -- выведет true, хотя планировалось false

Ложь только false и nil

Если бы в Python мы написали подобный код, как в примере ниже на Lua, то ожидали бы, что тела условных операторов не будут выполняться:

local a = ''
if a then
    print('Hello, ' .. a)
end

local b = 0
if b then
    print(b + 10)
end

local c = {}
if c then
    print('not empty')
end

Однако в Lua пустая строка, ноль и пустая таблица — это истина.

Hello, 
10
not empty

Ложью в Lua является только false и nil. Поэтому для примеров выше требуется более конкретизированная проверка значений:

local a = ''
if a ~= '' then
    print('Hello, ' .. a)
end

local b = 0
if b ~= 0 then
    print(b + 10)
end

local c = {}
if next(c) then
    print('not empty')
end

В Lua оператором неравенства является ~= (комбинация знаков тильды и равно). Функция next возвращает первый ключ таблицы. Но если таблица пустая, она вернет nil.