{}const=>[]async()letfn</>var
РазработкаLua

Таблицы в Lua: самая мощная структура, которую недооценивают новички

Разбираемся, почему таблицы — единственная структура данных в Lua — это не ограничение, а суперсила. Массивы, словари, объекты, классы и даже очереди — всё это таблицы. С примерами кода, метатаблицами, типичными ошибками новичков и мемами.

К

Кодик

Автор

calendar_today
schedule7 мин чтения

Когда ты приходишь в Lua после Python или JS и узнаёшь, что тут нет ни массивов, ни объектов, ни словарей… А есть только таблицы.

Твоё лицо: 😐

Лицо через неделю, когда понял мощь таблиц: 🤯

Давайте разбираться, почему таблицы в Lua — это не «бедность языка», а гениальный дизайн, который новички упорно недооценивают.

Подождите, тут правда только таблицы?

Да. Одна структура данных на все случаи жизни. Хочешь массив? Таблица. Словарь? Таблица. Объект с методами? Угадал — таблица. Очередь, стек, граф, дерево? Всё таблицы.

Lua-разработчик, объясняющий архитектуру проекта:

— А вот тут у нас таблица таблиц, которая хранит таблицы с таблицами.
— Понятно, спасибо.

Звучит как безумие, но на практике это невероятно элегантно.

🔥 100 000+ учеников уже с нами

Устал читать теорию?
Пора кодить!

Кодик — приложение, где ты учишься программировать через практику. AI-наставник, интерактивные уроки, реальные проекты.

🤖 AI 24/7
🎓 Сертификаты
💰 Бесплатно
🚀 Начать учиться
Присоединились сегодня

Таблица как массив: индексация с единицы (да, с ЕДИНИЦЫ)

Вот первый шок для любого, кто пришёл из мира нормальных языков:

local fruits = {"яблоко", "банан", "вишня"}

print(fruits[1])  -- "яблоко"
print(fruits[0])  -- nil 😭

Индексация с 1. Не с нуля. С ЕДИНИЦЫ.

Каждый бывший C-шник в этот момент чувствует физическую боль. Но у Lua есть свои причины — язык проектировался для людей, далёких от программирования (инженеры, дизайнеры), и для них «первый элемент = 1» — это логично.

Привыкаешь за пару дней. Страдаешь — первые два часа.

Полезная классика: перебор массива

local heroes = {"Lua", "Python", "JavaScript"}

for i, name in ipairs(heroes) do
    print(i .. ". " .. name)
end
-- 1. Lua
-- 2. Python
-- 3. JavaScript

ipairs — обходит по числовым индексам по порядку. Запомни его, он будет везде.

Таблица как словарь: ключ-значение на стероидах

Вот тут начинается магия. Таблица может одновременно быть массивом и словарём:

local player = {
    name = "ProGamer228",
    level = 42,
    hp = 100,
    "скрытое достижение"  -- это индекс [1]
}

print(player.name)     -- "ProGamer228"
print(player["level"]) -- 42
print(player[1])       -- "скрытое достижение"

Две формы доступа: через точку (player.name) и через скобки (player["name"]). Скобки нужны, когда ключ — переменная или содержит спецсимволы.

local key = "hp"
print(player[key])  -- 100

-- Ключом может быть вообще что угодно
local weird = {}
weird[true] = "да"
weird[3.14] = "пи"
weird[print] = "функция как ключ, почему бы и нет"

Ключом может быть любой тип, кроме nil. Это открывает безумные возможности, но злоупотреблять не стоит — читаемость кода тоже важна.

Длина таблицы: тут всё сложно

Оператор # возвращает длину «массивной» части таблицы:

local t = {10, 20, 30, 40}
print(#t)  -- 4 ✅

local mixed = {10, 20, nil, 40}
print(#mixed)  -- может быть 2, а может быть 4 🤡

Важное правило: если в массивной части есть nil — поведение # не определено. Lua может вернуть любое число. Это не баг, это by design. Просто не оставляй дырки в массивах.

Хочешь надёжную длину для словарей? Считай вручную:

local function tableLength(t)
    local count = 0
    for _ in pairs(t) do
        count = count + 1
    end
    return count
end

Таблица как объект: ООП по-луашному

В Lua нет классов. Но кого это останавливало?

local Dog = {}
Dog.__index = Dog

function Dog.new(name, breed)
    local self = setmetatable({}, Dog)
    self.name = name
    self.breed = breed
    return self
end

function Dog:bark()
    print(self.name .. " говорит: ГАВ!")
end

function Dog:info()
    print(self.name .. " — порода: " .. self.breed)
end

local rex = Dog.new("Рекс", "Овчарка")
rex:bark()  -- Рекс говорит: ГАВ!
rex:info()  -- Рекс — порода: Овчарка

Обрати внимание на двоеточие (:) вместо точки при вызове метода — это синтаксический сахар, который автоматически передаёт объект как self. Без этого пришлось бы писать rex.bark(rex), и это выглядело бы грустно.

Наследование? Тоже через таблицы

local Puppy = setmetatable({}, {__index = Dog})
Puppy.__index = Puppy

function Puppy.new(name, breed, toy)
    local self = Dog.new(name, breed)
    setmetatable(self, Puppy)
    self.toy = toy
    return self
end

function Puppy:play()
    print(self.name .. " играет с " .. self.toy .. "!")
end

local baby = Puppy.new("Бобик", "Корги", "мячиком")
baby:bark()  -- Бобик говорит: ГАВ! (унаследовано от Dog)
baby:play()  -- Бобик играет с мячиком!

Метатаблицы и __index — это сердце объектной системы Lua. Когда поле не найдено в текущей таблице, Lua заглядывает в __index метатаблицы. Цепочка может быть сколько угодно длинной — вот тебе и наследование.

Метаметоды: перегружаем вообще всё

Метатаблицы позволяют переопределить поведение таблицы для стандартных операций:

local Vector = {}
Vector.__index = Vector

function Vector.new(x, y)
    return setmetatable({x = x, y = y}, Vector)
end

-- Перегружаем оператор сложения
function Vector.__add(a, b)
    return Vector.new(a.x + b.x, a.y + b.y)
end

-- Перегружаем вывод через tostring
function Vector.__tostring(v)
    return "(" .. v.x .. ", " .. v.y .. ")"
end

local v1 = Vector.new(1, 2)
local v2 = Vector.new(3, 4)
local v3 = v1 + v2

print(tostring(v3))  -- (4, 6)

Основные метаметоды, которые стоит знать:

  • __add, __sub, __mul, __div — арифметика

  • __eq, __lt, __le — сравнение

  • __tostring — строковое представление

  • __index — доступ к несуществующему полю

  • __newindex — запись нового поля

  • __call — вызов таблицы как функции

  • __len — переопределение оператора #

С __call можно сделать таблицу вызываемой — и это реально используется в продакшене:

local Logger = setmetatable({}, {
    __call = function(self, message)
        print("[LOG] " .. os.date("%H:%M:%S") .. " — " .. message)
    end
})

Logger("Сервер запущен")  -- [LOG] 14:32:01 — Сервер запущен

Практические паттерны: таблицы в реальном коде

Конфиг через таблицу

local config = {
    server = {
        host = "localhost",
        port = 8080,
    },
    database = {
        name = "myapp",
        user = "admin",
        password = "hunter2",  -- классика
    },
    debug = true,
}

print(config.server.port)  -- 8080

Множество (Set)

local function Set(list)
    local set = {}
    for _, v in ipairs(list) do
        set[v] = true
    end
    return set
end

local langs = Set{"Lua", "Python", "JavaScript", "Lua"}

if langs["Lua"] then
    print("Lua в деле!")  -- Lua в деле!
end

if not langs["COBOL"] then
    print("COBOL? Не, не слышали")
end

Очередь

local Queue = {}
Queue.__index = Queue

function Queue.new()
    return setmetatable({first = 1, last = 0}, Queue)
end

function Queue:push(value)
    self.last = self.last + 1
    self[self.last] = value
end

function Queue:pop()
    if self.first > self.last then return nil end
    local value = self[self.first]
    self[self.first] = nil
    self.first = self.first + 1
    return value
end

local q = Queue.new()
q:push("первый")
q:push("второй")
print(q:pop())  -- "первый"
print(q:pop())  -- "второй"

Топ ошибок новичков с таблицами

1. Забыть, что таблицы передаются по ссылке

local a = {1, 2, 3}
local b = a       -- b указывает на ту же таблицу!
b[1] = 999
print(a[1])       -- 999 (сюрприз!)

Хочешь копию? Делай вручную:

local function shallowCopy(t)
    local copy = {}
    for k, v in pairs(t) do
        copy[k] = v
    end
    return copy
end

2. Путать pairs и ipairs

  • ipairs — только числовые ключи, по порядку, останавливается на первом nil

  • pairs — все ключи, порядок не гарантирован

3. Изменять таблицу во время итерации по ней

Не делай так. Просто не делай. Собирай изменения в отдельную таблицу, а потом применяй.

4. Считать # надёжным для таблиц с дырками

Уже говорили, но повторим: nil в середине массива = непредсказуемая длина. Без вариантов.

Изучай Lua и другие языки вместе с Кодик!

Если ты дочитал до этого места — тебе явно интересна не только теория, но и практика. Таблицы в Lua лучше всего усваиваются, когда ты пишешь код руками, а не просто читаешь статьи.

Кодик — это приложение, в котором можно изучать программирование с нуля и прокачивать навыки через практические задания. Python, JavaScript, HTML, CSS и другие технологии — всё с упором на то, чтобы ты реально писал код, а не просто смотрел на него.

А ещё у нас есть Telegram-канал с сообществом из 2000+ разработчиков, где регулярно выходят полезные посты, разборы задач и мемы про программирование (без них никуда). Отличный способ повторять материал в удобном месте — в очереди, в транспорте, за обедом. Подписывайся и прокачивайся каждый день.

Итого: почему таблицы — это гениально?

Таблицы в Lua — это швейцарский нож, который сначала кажется странным, а потом ты не понимаешь, как жил без него. Одна структура данных вместо десяти, минимум синтаксиса, максимум гибкости.

Вот что стоит запомнить:

  • Таблицы заменяют массивы, словари, объекты, классы и вообще всё

  • Индексация с 1 — не баг, а фича (просто прими это)

  • Метатаблицы превращают таблицы в полноценную объектную систему

  • Таблицы передаются по ссылке — будь внимателен с копированием

  • # работает надёжно только для массивов без дырок

Lua — это язык, который доказывает: меньше — значит больше. И таблицы — лучший пример этой философии.

Теперь иди и напиши что-нибудь на Lua. Таблицы ждут. 🚀

🎯Хватит откладывать

Понравилась статья?
Пора применять на практике!

В Кодик ты не просто читаешь — ты сразу пишешь код. Теория + практика = реальный скилл.

Мгновенная практика
🧠AI объяснит код
🏆Сертификат

Без регистрации • Без карты