LEARN X · ЗА 13 МИН

Lua

Экспресс-тур по Lua: весь язык на одной странице в комментариях кода — таблицы, метатаблицы, ООП, замыкания, корутины и стандартная библиотека.

Lua — маленький, быстрый и встраиваемый язык: игры (Roblox, World of Warcraft), конфиги (Nginx, Redis), скрипты. Минимум синтаксиса, одна структура данных — таблица. Здесь весь язык за 13 минут, прямо в комментариях кода.

1. Вывод и комментарии

-- Однострочный комментарий начинается с двух дефисов

--[[
  Многострочный комментарий
  занимает несколько строк
]]

print("Привет, Lua!")   -- Привет, Lua!
print(1, 2, 3)           -- 1	2	3   (аргументы через табуляцию)
io.write("без переноса") -- печатает без \n в конце

2. Переменные и типы

Типизация динамическая: тип у значения, а не у переменной.

-- local делает переменную локальной (всегда так и делайте!)
local x = 10        -- без local переменная глобальная
local name = "Аня"

-- nil — отсутствие значения (как null)
local nothing = nil
print(nothing)      -- nil

-- boolean: только nil и false ложны, ВСЁ остальное истинно (даже 0 и "")
local flag = true

-- number — одно числовое: и целые, и дробные (в Lua 5.3+ есть подтипы)
local n = 42
local pi = 3.14
local hex = 0xFF    -- 255

-- string — строки
local s = "текст"

-- type() возвращает имя типа строкой
print(type(x))      -- number
print(type(name))   -- string
print(type(nil))    -- nil
print(type(true))   -- boolean
print(type(print))  -- function

-- Множественное присваивание
local a, b, c = 1, 2, 3
a, b = b, a         -- обмен значениями без temp

3. Строки

-- Конкатенация — оператор .. (точка-точка), НЕ +
local hello = "При" .. "вет"   -- "Привет"
local mix = "Год: " .. 2026     -- число само превратится в строку

-- Многострочные строки в двойных квадратных скобках
local text = [[
Первая строка
Вторая строка
]]

-- Длина строки — оператор #
print(#"Lua")               -- 3

-- Методы из библиотеки string
print(string.upper("lua"))  -- LUA
print(string.lower("LUA"))  -- lua
print(string.len("Lua"))    -- 3
print(string.sub("Привет", 1, 3)) -- первые символы (байты!)
print(string.rep("ab", 3))  -- ababab
print(string.format("%d + %d = %d", 2, 2, 4)) -- 2 + 2 = 4

-- Можно вызывать методы прямо на строке через двоеточие
print(("lua"):upper())      -- LUA

-- Поиск и замена (шаблоны Lua, не regex)
print(string.find("hello", "ll"))  -- 3	4
print(string.gsub("hello", "l", "L")) -- heLLo	2 (строка и число замен)

4. Операторы и условия

-- Арифметика
print(7 + 2)   -- 9
print(7 - 2)   -- 5
print(7 * 2)   -- 14
print(7 / 2)   -- 3.5  (деление всегда даёт дробь)
print(7 % 2)   -- 1    (остаток)
print(7 // 2)  -- 3    (целочисленное деление, Lua 5.3+)
print(2 ^ 10)  -- 1024 (возведение в степень)

-- Сравнение: равно ==, НЕ равно ~= (тильда-равно, не !=)
print(1 == 1)  -- true
print(1 ~= 2)  -- true
print(3 < 5)   -- true
print(5 >= 5)  -- true

-- Логические: and, or, not (слова, не && ||)
print(true and false)  -- false
print(true or false)   -- true
print(not true)        -- false

-- and/or возвращают операнд, а не строго boolean — удобно для значений по умолчанию
local name = nil
local display = name or "Гость"  -- "Гость"
print(display)

-- Условие: if / elseif / else / end
local n = 7
if n > 10 then
  print("много")
elseif n > 5 then
  print("средне")   -- сработает это
else
  print("мало")
end

5. Циклы

-- while — проверка ДО тела
local i = 1
while i <= 3 do
  print(i)   -- 1, 2, 3
  i = i + 1  -- нет ++ и += в базовом Lua
end

-- repeat / until — проверка ПОСЛЕ тела (выполнится минимум раз)
local j = 1
repeat
  print(j)
  j = j + 1
until j > 3   -- 1, 2, 3

-- numeric for: от, до (включительно), шаг (по умолчанию 1)
for k = 1, 5 do print(k) end      -- 1..5
for k = 10, 1, -2 do print(k) end -- 10, 8, 6, 4, 2

-- generic for с ipairs — по массиву (индексы 1, 2, 3... до первого nil)
local fruits = {"яблоко", "груша", "слива"}
for index, value in ipairs(fruits) do
  print(index, value)  -- 1 яблоко / 2 груша / 3 слива
end

-- generic for с pairs — по всем ключам таблицы (порядок не гарантирован)
local person = {name = "Аня", age = 30}
for key, value in pairs(person) do
  print(key, value)    -- name Аня / age 30 (порядок любой)
end

-- break прерывает цикл; continue в Lua НЕТ (используют goto или вложенный if)

6. Таблицы

Таблица — единственная структура данных в Lua. Это и массив, и словарь, и объект.

-- Как массив (индексация с 1, а не с 0!)
local arr = {"a", "b", "c"}
print(arr[1])   -- a   (первый элемент — индекс 1)
print(arr[3])   -- c
print(#arr)     -- 3   (# — длина массива)
arr[4] = "d"    -- добавили четвёртый

-- Как словарь (ключ-значение)
local user = {
  name = "Иван",
  age = 25,
  ["любимый цвет"] = "синий",  -- ключ с пробелом — в скобках
}
print(user.name)          -- Иван   (точечный доступ)
print(user["age"])        -- 25     (доступ по строке-ключу)
user.email = "[email protected]"  -- добавили поле
user.age = nil            -- удалили поле (присвоили nil)

-- Смешанная таблица — и массив, и словарь сразу
local mixed = {10, 20, 30, type = "числа"}
print(mixed[1])     -- 10
print(mixed.type)   -- числа

-- Вложенные таблицы
local data = {
  users = {
    {name = "A"},
    {name = "B"},
  },
}
print(data.users[2].name)  -- B

7. Функции

-- Объявление
function greet(name)
  return "Привет, " .. name
end
print(greet("Lua"))  -- Привет, Lua

-- Функция — значение, её можно положить в переменную
local square = function(x) return x * x end
print(square(5))     -- 25

-- Множественный возврат
function minmax(a, b)
  if a < b then return a, b else return b, a end
end
local lo, hi = minmax(8, 3)
print(lo, hi)        -- 3 8

-- Переменное число аргументов (variadic) через ...
function sum(...)
  local total = 0
  for _, v in ipairs({...}) do  -- ... упаковываем в таблицу
    total = total + v
  end
  return total
end
print(sum(1, 2, 3, 4))  -- 10

-- Замыкания: внутренняя функция помнит локальные переменные внешней
function counter()
  local count = 0
  return function()
    count = count + 1   -- count живёт между вызовами
    return count
  end
end
local next = counter()
print(next())  -- 1
print(next())  -- 2
print(next())  -- 3

8. Таблицы как объекты

Двоеточие : — это сахар: оно неявно передаёт таблицу первым аргументом self.

local account = {balance = 100}

-- Метод через двоеточие: внутри доступен self (сама таблица)
function account:deposit(amount)
  self.balance = self.balance + amount
end

-- Вызов тоже через двоеточие
account:deposit(50)
print(account.balance)  -- 150

-- account:deposit(50) — это сахар для account.deposit(account, 50)
-- Двоеточие при объявлении и вызове просто прячет первый аргумент self
function account.withdraw(self, amount)  -- эквивалент с точкой
  self.balance = self.balance - amount
end
account:withdraw(30)
print(account.balance)  -- 120

9. Метатаблицы и ООП

У таблиц нет встроенных классов. ООП собирают вручную из метатаблиц и поля __index.

-- Метатаблица меняет поведение таблицы через метаметоды (__index, __add, ...)
local Animal = {}
Animal.__index = Animal  -- ищем недостающие поля в самом Animal

-- "Конструктор"
function Animal.new(name, sound)
  local self = setmetatable({}, Animal)  -- привязываем метатаблицу
  self.name = name
  self.sound = sound
  return self
end

function Animal:speak()
  return self.name .. " говорит " .. self.sound
end

local cat = Animal.new("Кот", "мяу")
print(cat:speak())  -- Кот говорит мяу

-- Наследование: метатаблица потомка ссылается на родителя
local Dog = setmetatable({}, {__index = Animal})
Dog.__index = Dog

function Dog.new(name)
  local self = Animal.new(name, "гав")  -- зовём родительский конструктор
  return setmetatable(self, Dog)
end

function Dog:fetch()  -- свой метод
  return self.name .. " принёс мячик"
end

local d = Dog.new("Рекс")
print(d:speak())  -- Рекс говорит гав   (унаследовано от Animal)
print(d:fetch())  -- Рекс принёс мячик

-- Перегрузка операторов через метаметоды
local Vec = {}
Vec.__index = Vec
Vec.__add = function(a, b)   -- что делает оператор + для Vec
  return setmetatable({x = a.x + b.x, y = a.y + b.y}, Vec)
end
local v = setmetatable({x = 1, y = 2}, Vec) + setmetatable({x = 3, y = 4}, Vec)
print(v.x, v.y)  -- 4 6

10. Стандартная библиотека

-- table — работа с таблицами-массивами
local t = {"a", "b", "c"}
table.insert(t, "d")        -- добавить в конец: {a,b,c,d}
table.insert(t, 1, "z")     -- вставить по индексу: {z,a,b,c,d}
table.remove(t, 1)          -- удалить по индексу (вернёт удалённое)
table.remove(t)             -- удалить последний
print(table.concat({"a","b","c"}, "-"))  -- a-b-c (склейка в строку)
local nums = {3, 1, 2}
table.sort(nums)            -- сортировка на месте: {1, 2, 3}
print(nums[1])              -- 1

-- math — математика
print(math.floor(3.7))   -- 3
print(math.ceil(3.2))    -- 4
print(math.abs(-5))      -- 5
print(math.max(1, 9, 4)) -- 9
print(math.min(1, 9, 4)) -- 1
print(math.sqrt(16))     -- 4.0
print(math.pi)           -- 3.1415926535898
math.randomseed(os.time())
print(math.random(1, 6)) -- случайное от 1 до 6

-- string — уже видели: format, sub, gsub, find, upper, lower, rep, len

-- os — операционная система (кратко)
print(os.time())         -- метка времени (секунды с эпохи)
print(os.date("%Y-%m-%d")) -- текущая дата, напр. 2026-06-16
print(os.clock())        -- процессорное время (для замеров)

11. Обработка ошибок

-- error() выбрасывает ошибку
local function divide(a, b)
  if b == 0 then
    error("деление на ноль")
  end
  return a / b
end

-- pcall (protected call) ловит ошибку и не роняет программу
-- Возвращает: успех (boolean) + результат ИЛИ текст ошибки
local ok, result = pcall(divide, 10, 2)
print(ok, result)   -- true  5.0

local ok2, err = pcall(divide, 10, 0)
print(ok2, err)     -- false  деление на ноль (с префиксом файла/строки)

-- Типичный паттерн обработки
if ok2 then
  print("Результат: " .. err)
else
  print("Ошибка: " .. err)  -- Ошибка: ...деление на ноль
end

-- xpcall добавляет обработчик-трейсбек
local ok3 = xpcall(function()
  error("что-то сломалось")
end, function(e)
  return "перехвачено: " .. e
end)
print(ok3)  -- false
Поддержать проект