Основы языка программирования Lua

Этот курс погружает в изучение легковесного и гибкого скриптового языка Lua, популярного в разработке игр и встраиваемых системах. Студенты освоят синтаксис, работу с таблицами, метапрограммирование и основы интеграции с C API.

1. Введение в Lua: окружение, переменные и управляющие конструкции

Введение в Lua: окружение, переменные и управляющие конструкции

Добро пожаловать в курс «Основы языка программирования Lua». Это первая статья, с которой начнется ваше погружение в мир одного из самых элегантных и быстрых скриптовых языков программирования. Lua (в переводе с португальского — «Луна») был создан в 1993 году в Папском католическом университете Рио-де-Жанейро. Сегодня он используется повсюду: от разработки игр (Roblox, World of Warcraft) до встраиваемых систем и конфигурации сложного программного обеспечения.

В этой статье мы разберем, как подготовить рабочее окружение, изучим базовый синтаксис, работу с переменными и научимся управлять потоком выполнения программы.

Почему Lua?

Прежде чем писать код, важно понять философию языка. Lua проектировался как встраиваемый язык. Это означает, что он часто работает не сам по себе, а внутри другой большой программы (написанной, например, на C или C++), управляя её логикой.

!Схема взаимодействия движка на C++ и скриптов на Lua, показывающая роль языка как связующего звена.

Основные преимущества Lua: * Компактность: Весь интерпретатор занимает сотни килобайт. * Скорость: Lua считается одним из самых быстрых интерпретируемых языков, особенно при использовании JIT-компиляции (LuaJIT). * Простота: Синтаксис минималистичен и интуитивно понятен.

Установка и окружение

Чтобы начать программировать на Lua, вам понадобится интерпретатор. Это программа, которая читает ваш код и выполняет его.

Варианты запуска

  • Онлайн-компиляторы: Если вы не хотите ничего устанавливать, можно использовать сервисы вроде Replit или Lua Demo. Это отличный вариант для первых экспериментов.
  • Локальная установка:
  • * Windows: Скачайте бинарные файлы с официального сайта или используйте пакетный менеджер (например, Chocolatey: choco install lua). * macOS: Используйте Homebrew: brew install lua. * Linux: Обычно Lua доступен в репозиториях: sudo apt install lua5.3 (или новее).

    После установки откройте терминал (командную строку) и введите lua. Если вы увидите приветственное сообщение и версию языка, значит, всё работает. Вы попали в интерактивный режим.

    Попробуйте ввести первую команду:

    Нажмите Enter. Система ответит вам выводом текста на экран. Поздравляем, ваша первая программа написана!

    Для написания больших программ интерактивный режим неудобен. Обычно код пишут в текстовом файле с расширением .lua (например, script.lua), а затем запускают его через терминал командой:

    Основы синтаксиса и комментарии

    Lua — язык с динамической типизацией и свободным форматом. Это значит, что вам не нужно заранее объявлять типы переменных, а пробелы и переносы строк (в разумных пределах) не влияют на выполнение.

    Важная особенность: точка с запятой ; в конце строк не обязательна. Вы можете ставить её для разделения нескольких команд на одной строке, но обычно её опускают.

    Комментарии

    Хороший код должен быть понятен не только машине, но и человеку. Для этого используются комментарии, которые игнорируются интерпретатором.

    * Однострочный комментарий начинается с двух дефисов --. * Многострочный комментарий начинается с --[[ и заканчивается ]].

    Переменные и типы данных

    В Lua переменные не имеют типа, тип имеют только значения. Это фундаментальное отличие от языков вроде C++ или Java.

    Глобальные и локальные переменные

    По умолчанию все переменные в Lua — глобальные. Это одна из особенностей, за которую новичков часто ругают опытные разработчики. Глобальные переменные видны во всей программе, что может привести к ошибкам.

    Чтобы создать переменную, ограниченную текущим блоком кода (файлом, функцией или циклом), используется ключевое слово local. Всегда старайтесь использовать local, если нет веской причины делать переменную глобальной.

    Базовые типы данных

    Lua имеет всего 8 базовых типов. Сегодня мы рассмотрим самые важные из них для старта.

  • Nil (ничто): Тип значения nil. Означает отсутствие значения. Если вы обратитесь к переменной, которую не создавали, вы получите nil. Присваивание переменной nil удаляет её.
  • Boolean (логический): Имеет значения true (истина) и false (ложь).
  • > Важно: В Lua только false и nil считаются ложью. Ноль 0 и пустая строка "" считаются истиной (true). Это частая ловушка для программистов, пришедших из C или Python.
  • Number (число): Представляет вещественные числа (с плавающей точкой). Начиная с версии Lua 5.3, введен подтип для целых чисел (integer), но для разработчика это часто происходит прозрачно.
  • String (строка): Последовательность символов. Строки можно заключать в одинарные ' или двойные " кавычки.
  • Пример работы с типами:

    Операторы

    Lua поддерживает стандартный набор операторов.

    Арифметические операторы

    * + (сложение) * - (вычитание) (умножение) * / (деление) * // (целочисленное деление, отбрасывает дробную часть, доступно в Lua 5.3+) * % (остаток от деления) * ^ (возведение в степень)

    Математически операцию остатка от деления можно представить так:

    Где — остаток, — делимое, — делитель, а — операция округления вниз до ближайшего целого.

    Операторы сравнения

    Они всегда возвращают true или false.

    * == (равно) * ~= (не равно) — обратите внимание, в Lua используется тильда, а не восклицательный знак !=. * <, >, <=, >= (стандартные неравенства)

    Логические операторы

    * and (и): возвращает первый ложный аргумент или последний, если оба истинны. * or (или): возвращает первый истинный аргумент или последний, если оба ложны. * not (не): инвертирует значение.

    Пример:

    Управляющие конструкции

    Программа редко выполняется линейно сверху вниз. Нам часто нужно ветвление и повторение действий.

    Условный оператор if

    Конструкция if позволяет выполнять код только при выполнении условия. Блок обязательно заканчивается ключевым словом end.

    Обратите внимание на синтаксис: if ... then ... elseif ... then ... else ... end. Слово elseif пишется слитно.

    Циклы

    Циклы позволяют выполнять блок кода многократно.

    #### Цикл while

    Выполняется, пока условие истинно. Проверка условия происходит перед итерацией.

    #### Цикл repeat...until

    Аналог цикла do...while в других языках. Тело цикла выполняется минимум один раз, так как проверка условия происходит в конце. Цикл повторяется, пока условие ложно (until = до тех пор, пока не станет истиной).

    #### Числовой цикл for

    Самый популярный цикл. Он изменяет переменную-счетчик от начального значения до конечного с определенным шагом.

    Синтаксис: for variable = start, stop, step do ... end

    Если step не указан, он считается равным 1.

    Количество итераций в цикле for можно рассчитать по формуле:

    Где — конечное значение, — начальное значение, — шаг цикла, а — округление вниз. Функция гарантирует, что количество итераций не будет отрицательным.

    !Сравнение логики выполнения циклов с предусловием и постусловием.

    Область видимости в блоках

    Важно помнить, что управляющие конструкции (if, while, for, function) создают свои блоки видимости. Если вы объявите local переменную внутри цикла, она не будет видна снаружи.

    Заключение

    Мы рассмотрели фундамент языка Lua: как запустить код, как создавать переменные и как управлять потоком выполнения программы. Lua подкупает своей простотой, но за этой простотой скрывается мощный инструмент.

    В следующей статье мы перейдем к одной из самых мощных и уникальных особенностей Lua — таблицам (tables), которые заменяют массивы, словари, объекты и модули.

    2. Таблицы как универсальная структура данных: массивы и словари

    Таблицы как универсальная структура данных: массивы и словари

    Приветствую вас, студенты! В прошлой лекции мы познакомились с переменными, типами данных и управляющими конструкциями. Вы научились писать простую логику. Но что, если вам нужно хранить не одно число, а список из тысячи имен пользователей? Или базу данных инвентаря персонажа в игре? Создавать тысячу переменных name1, name2... name1000 — путь в никуда.

    Сегодня мы изучим таблицы (tables). В большинстве языков программирования (таких как C++, Java или Python) есть множество структур данных: массивы, списки, словари, множества, объекты, хэш-карты. В Lua всё иначе. В Lua есть только одна структура данных — таблица. Но она настолько гибка и универсальна, что заменяет собой всё вышеперечисленное.

    Что такое таблица в Lua?

    Таблица в Lua — это ассоциативный массив. Это означает, что она хранит пары «ключ — значение». Вы можете представить таблицу как огромный шкаф с ящиками, где на каждом ящике есть уникальная наклейка (ключ), а внутри лежит какой-то предмет (значение).

    !Визуальная метафора таблицы Lua, объединяющей свойства массива и словаря.

    Создание таблицы

    Таблицы создаются с помощью фигурных скобок {}. Это называется конструктором таблицы.

    Важно понимать: переменная myTable хранит не саму таблицу, а ссылку на неё. Если вы скопируете переменную, вы скопируете только адрес в памяти, а не данные.

    Таблицы как массивы (Списки)

    Самый простой способ использования таблицы — это обычный список (или массив). В этом случае ключами выступают порядковые номера элементов (целые числа).

    Особенность индексации в Lua

    > Внимание! Это самый важный момент для тех, кто пришел из других языков. Индексация массивов в Lua начинается с 1, а не с 0.

    Если в Python или C++ первый элемент — это array[0], то в Lua — это array[1]. Это сделано намеренно, так как Lua создавался в научной среде, где в математической записи векторов и матриц принято начинать отсчет с единицы.

    Длина массива

    Чтобы узнать количество элементов в массиве (его длину), используется оператор # (решетка).

    Итерация по массиву (ipairs)

    Чтобы перебрать все элементы списка по порядку, используется цикл for и специальная функция-итератор ipairs.

    Этот код последовательно выведет всех героев. ipairs гарантирует, что элементы будут обработаны от 1 до конца списка без пропусков.

    Таблицы как словари (Хэш-карты)

    Часто нам нужно обращаться к данным не по номеру, а по имени. Например, хранить свойства персонажа: имя, уровень, здоровье. В Lua это делается так же просто.

    Синтаксический сахар

    Lua предоставляет удобный способ обращения к строковым ключам через точку. Запись player.name абсолютно эквивалентна player["name"].

    Это работает только если ключ является строкой и соответствует правилам именования переменных (нет пробелов, не начинается с цифры). Если ключ содержит пробелы, например "max speed", придется использовать квадратные скобки: player["max speed"].

    Любые ключи

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

    Итерация по словарю (pairs)

    Для перебора словарей используется функция pairs. В отличие от ipairs, она не гарантирует порядок перебора, так как словари внутри устроены как хэш-таблицы.

    Внутреннее устройство: немного математики

    Почему поиск по ключу (например, player["name"]) происходит так быстро, даже если в таблице миллионы записей? Lua использует структуру данных, называемую хэш-таблицей.

    Когда вы пишете ключ, интерпретатор превращает его в число (хэш) и вычисляет индекс в памяти, где лежит значение. Упрощенно формулу вычисления индекса можно представить так:

    Где: * — итоговый индекс ячейки в памяти, куда будет положено значение. * — ваш ключ (например, строка "name"). * — хэш-функция, которая преобразует ключ в большое целое число. * — размер выделенной памяти под таблицу. * — операция взятия остатка от деления на размер памяти.

    Благодаря этой математической операции, компьютер сразу знает, в какую ячейку идти, не перебирая весь список элементов.

    Смешанные таблицы

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

    Библиотека table

    Lua предоставляет стандартную библиотеку table для манипуляций с массивами.

    Вставка и удаление

    * table.insert(t, value) — добавляет элемент в конец массива. * table.insert(t, index, value) — вставляет элемент в указанную позицию, сдвигая остальные. * table.remove(t, index) — удаляет элемент по индексу, сдвигая остальные, чтобы закрыть «дыру».

    Сортировка

    Функция table.sort(t) сортирует массив на месте (изменяет саму таблицу).

    Частые ошибки и «Дыры»

    Одна из самых коварных проблем в Lua — это «дыры» в массивах. Если вы присвоите элементу внутри массива значение nil, он исчезнет.

    В этом случае оператор длины #t может вести себя непредсказуемо, так как он считает границей массива место, где последовательность прерывается. Никогда не используйте nil внутри массивов, если планируете использовать оператор # или ipairs. Для удаления используйте table.remove.

    Заключение

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

    В следующей статье мы перейдем к функциям: научимся создавать подпрограммы, возвращать значения и поймем, почему в Lua функции — это тоже значения, которые можно... хранить в таблицах!

    3. Функции первого класса, замыкания и модули

    Функции первого класса, замыкания и модули

    Здравствуйте, коллеги! Мы продолжаем наш курс по изучению Lua. В прошлых лекциях мы разобрали фундамент: переменные, циклы и мощнейшую структуру данных — таблицы. Теперь пришло время вдохнуть в наши программы настоящую жизнь и гибкость.

    Сегодняшняя тема — одна из самых красивых в Lua. Мы поговорим о том, почему функции в этом языке — это не просто куски кода, а полноценные данные. Мы разберем магию замыканий (closures), которая позволяет функциям «помнить» данные, и научимся структурировать код с помощью модулей.

    Функции как значения первого класса

    В большинстве старых языков программирования (например, в классическом Pascal или C) функция — это статическая сущность. Она существует где-то в коде, её можно вызвать, но с ней нельзя обращаться как с числом или строкой.

    В Lua функции являются значениями первого класса (first-class values). Это означает, что функция имеет те же права, что и число 10, строка "Hello" или таблица.

    Что мы можем делать с функциями?

  • Присваивать их переменным.
  • Хранить их в таблицах.
  • Передавать их как аргументы в другие функции.
  • Возвращать их как результат работы других функций.
  • Анонимные функции

    Рассмотрим привычное объявление функции:

    На самом деле, это «синтаксический сахар». Для Lua этот код эквивалентен следующему:

    Здесь function(x) ... end — это анонимная функция (или лямбда). Мы создаем функцию и тут же кладем её в переменную square. Сама по себе функция имени не имеет, имя есть только у переменной, которая на неё ссылается.

    Функции высшего порядка

    Раз функции — это значения, мы можем передавать их в другие функции. Функции, которые принимают другие функции в качестве аргументов, называются функциями высшего порядка.

    Классический пример — сортировка таблицы. Допустим, у нас есть список студентов, и мы хотим отсортировать их по возрасту в обратном порядке.

    Здесь мы не писали отдельную функцию сортировки, мы просто передали логику сравнения «на лету».

    Давайте рассмотрим математический пример использования функции высшего порядка. Напишем функцию, которая вычисляет сумму значений любой другой функции в диапазоне от до .

    Математически это записывается как сумма:

    Где: * — итоговая сумма. * — переменная счетчика, изменяющаяся от до . * — нижняя граница диапазона. * — верхняя граница диапазона. * — функция, применяемая к каждому числу .

    Реализация на Lua:

    Лексическая область видимости и Замыкания

    Теперь перейдем к самой сложной, но и самой мощной концепции этой лекции.

    Лексическая область видимости (Lexical Scoping)

    В Lua функции могут быть вложенными. Функция, написанная внутри другой функции, имеет доступ ко всем локальным переменным внешней функции. Эти переменные для внутренней функции называются upvalues (внешние локальные переменные).

    Что такое замыкание?

    Представьте, что вы создали функцию внутри другой функции, и эта внутренняя функция использует переменную родителя. Когда родительская функция завершает работу, её локальные переменные должны исчезнуть из памяти. Но если мы вернем внутреннюю функцию наружу, она «утащит» с собой необходимые ей переменные.

    Замыкание (Closure) — это функция плюс все внешние локальные переменные (upvalues), которые она использует.

    !Визуальная метафора замыкания: функция носит свои переменные с собой, как в рюкзаке.

    Рассмотрим классический пример — счетчик.

    Почему это важно? Переменная count является локальной для newCounter. К ней нельзя обратиться из глобальной области видимости напрямую. Но функция c1 имеет к ней доступ. Это позволяет создавать приватные переменные, защищенные от внешнего вмешательства. Это основа инкапсуляции в Lua.

    Модули

    Когда программа разрастается до сотен строк, держать всё в одном файле становится невозможно. Нам нужно разбивать код на логические части — модули.

    В Lua модуль — это, по сути, таблица, заполненная функциями и константами, которая возвращается из отдельного файла.

    Создание модуля

    Создадим файл mymath.lua:

    Использование модуля

    Теперь в основном файле main.lua мы можем подключить наш модуль с помощью функции require.

    Функция require делает следующее:

  • Ищет файл с указанным именем.
  • Выполняет его.
  • Возвращает то, что вернул файл (в нашем случае таблицу M).
  • Запоминает результат, чтобы при повторном вызове require не загружать файл заново.
  • Формула площади круга

    В примере выше мы использовали формулу площади круга. Давайте запишем её формально:

    Где: * — площадь круга. * — математическая константа (примерно 3.14159). * — радиус круга.

    Благодаря замыканиям и области видимости, переменная pi в нашем модуле остается приватной. Если пользователь модуля захочет изменить число Пи, он не сможет сделать это случайно, так как у него нет прямого доступа к локальной переменной pi внутри файла mymath.lua.

    Хранение функций в таблицах

    Поскольку модули — это просто таблицы, мы приходим к выводу, что Lua не имеет специального типа "класс" или "объект". Но мы можем эмулировать их.

    Это очень похоже на статические методы классов в других языках. В будущих лекциях мы узнаем, как добавить сюда двоеточие : и превратить это в полноценное объектно-ориентированное программирование с наследованием.

    Заключение

    Сегодня мы сделали огромный шаг вперед. Мы узнали, что: * Функции в Lua — это данные, которые можно передавать и возвращать. * Замыкания позволяют функциям «запоминать» состояние, создавая приватные хранилища данных. * Модули — это простой способ организации кода, основанный на таблицах и локальных переменных.

    Эти инструменты позволяют писать чистый, модульный и безопасный код. В следующей статье мы погрузимся в тему мета-таблиц (metatables) — механизма, который позволяет изменять поведение стандартных операций (например, научить Lua складывать две таблицы знаком +).

    4. Метатаблицы, метаметоды и реализация ООП

    Метатаблицы, метаметоды и реализация ООП

    Приветствую вас, друзья! Мы продолжаем наше путешествие по языку Lua. В предыдущих статьях мы изучили таблицы как универсальные хранилища данных и функции как строительные блоки логики. Мы даже научились создавать модули. Но до сих пор наши таблицы были «пассивными»: они просто хранили данные.

    Сегодня мы превратим их в «умные» объекты. Мы узнаем, как научить Lua складывать две таблицы знаком +, как заставить таблицу вести себя как функция и, самое главное, как реализовать полноценное Объектно-Ориентированное Программирование (ООП) в языке, где классов официально не существует.

    Тема сегодняшнего урока — метатаблицы.

    Что такое метатаблица?

    В Lua каждая таблица может иметь свою «тень» — другую таблицу, которая описывает её поведение в нестандартных ситуациях. Эта «тень» называется метатаблицей.

    Представьте, что вы пытаетесь сложить две таблицы:

    Когда Lua сталкивается с операцией, которую он не знает как выполнить (например, сложение таблиц), он проверяет: «А есть ли у этой таблицы метатаблица? И есть ли в ней инструкция на этот случай?».

    !Визуализация концепции метатаблицы как набора инструкций для обработки нестандартных операций.

    Установка метатаблицы

    Для связывания обычной таблицы и метатаблицы используется функция setmetatable(table, metatable).

    Теперь myMeta управляет поведением myTable.

    Метаметоды: учим таблицы математике

    Инструкции внутри метатаблицы называются метаметодами. Это специальные поля, названия которых начинаются с двух подчеркиваний __.

    Давайте создадим систему для работы с векторами на плоскости. Вектор — это направленный отрезок, имеющий координаты и .

    Математически сложение двух векторов и выглядит так:

    Где: * — результирующий вектор. * и — исходные векторы. * — координаты векторов по оси X. * — координаты векторов по оси Y.

    Реализуем это в Lua:

    Популярные метаметоды

    Lua поддерживает множество событий:

    __add (+), __sub (-), __mul (), __div (/) * __eq (проверка на равенство ==) * __lt (меньше <), __le (меньше или равно <=) * __tostring (преобразование в строку)

    Остановимся на __tostring. Если вы попытаетесь сделать print(vec3), вы увидите что-то вроде table: 0x56a2b0. Это адрес памяти, что не очень информативно. Исправим это:

    Ключ к ООП: метаметод __index

    Самый важный метаметод для реализации классов — это __index. Он срабатывает, когда мы пытаемся прочитать ключ, которого в таблице нет.

    Логика работы Lua:

  • Вы запрашиваете table.key.
  • Lua ищет key в table. Если находит — возвращает значение.
  • Если не находит, Lua смотрит в метатаблицу на поле __index.
  • Если __index — это другая таблица, Lua ищет ключ в ней.
  • Если __index — это функция, Lua вызывает её.
  • !Иллюстрация механизма наследования через __index, показывающая цепочку поиска свойств.

    Именно этот механизм позволяет нам создавать прототипы и классы.

    Реализация ООП в Lua

    В Lua нет ключевого слова class. Но благодаря таблицам и __index, мы можем их имитировать. Такой подход называется прототипным программированием (похоже на JavaScript до стандарта ES6).

    Шаг 1: Создание класса (Прототипа)

    Создадим класс Account (Банковский счет).

    Обратите внимание на двоеточие :. В Lua запись function Account:withdraw(v) — это синтаксический сахар для function Account.withdraw(self, v). Параметр self указывает на объект, с которым мы работаем.

    Шаг 2: Конструктор (new)

    Теперь нам нужно научиться создавать новые объекты, которые будут наследовать методы от Account.

    Разберем магическую строку self.__index = self:

  • Мы устанавливаем Account (это self) как метатаблицу для нового объекта o.
  • Мы говорим: «Если в объекте o чего-то нет, ищи это в Account» (через __index).
  • Шаг 3: Использование объектов

    Когда мы вызываем a:deposit(500):

  • Lua ищет функцию deposit в таблице a.
  • Не находит.
  • Идет в метатаблицу (это Account).
  • Смотрит в __index (это снова Account).
  • Находит deposit в Account и вызывает её, передавая a как self.
  • Наследование

    Поскольку класс — это просто таблица, мы можем унаследовать от неё другой класс, используя тот же метод new.

    Создадим SpecialAccount, который позволяет уходить в минус.

    Здесь цепочка поиска выглядит так: s -> SpecialAccount -> Account.

    Оптимизация и производительность

    Использование метатаблиц немного медленнее, чем прямой доступ к полям, но это плата за гибкость и читаемость кода. В реальных проектах (например, при разработке игр на LÖVE или Defold) этот оверхед (накладные расходы) обычно незначителен по сравнению с выгодой от структурированного кода.

    Заключение

    Мы разобрали одну из самых сложных, но интересных тем в Lua. Метатаблицы превращают Lua из простого скриптового языка в мощный инструмент для построения сложных архитектур.

    Теперь вы знаете:

  • Как перегружать операторы с помощью метаметодов (__add, __tostring).
  • Как работает поиск отсутствующих ключей через __index.
  • Как реализовать классы, объекты и наследование.
  • В следующей статье мы поговорим о корутинах (coroutines) — уникальном механизме Lua для реализации многозадачности, который позволяет ставить выполнение функций на паузу.

    5. Стандартные библиотеки и основы C API

    Стандартные библиотеки и основы C API

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

    Сегодня мы рассмотрим две фундаментальные темы, которые превращают Lua из игрушки в профессиональный инструмент:

  • Стандартные библиотеки: набор готовых инструментов, которые идут в комплекте с языком.
  • C API: интерфейс, позволяющий Lua взаимодействовать с языком C (и C++). Именно благодаря этому Lua стал стандартом в геймдеве и встраиваемых системах.
  • Стандартные библиотеки Lua

    Философия Lua — минимализм. Ядро языка очень компактное, но оно расширяется за счет стандартных библиотек. Они предоставляют функции для работы со строками, таблицами, математикой, вводом-выводом и операционной системой.

    !Обзор основных модулей стандартной библиотеки Lua.

    Библиотека String: работа с текстом

    В отличие от многих языков, где строки являются объектами с методами (например, str.length()), в Lua функции для работы со строками собраны в модуле string. Однако, благодаря метатаблицам, которые мы изучили ранее, вы можете использовать сокращенную запись через двоеточие.

    #### Шаблоны (Patterns)

    Самая мощная часть библиотеки string — это шаблоны. Это упрощенный аналог регулярных выражений (RegEx). Lua использует свой синтаксис, так как полноценный движок RegEx занял бы больше места, чем весь интерпретатор Lua.

    Основные классы символов: * . — любой символ. * %a — буква (letter). * %d — цифра (digit). * %s — пробельный символ (space). * %w — буквенно-цифровой символ (word).

    Заглавная буква в классе означает инверсию (например, %D — всё, кроме цифры).

    Пример извлечения даты из строки:

    Библиотека Table: манипуляции с данными

    Модуль table предоставляет функции для работы с массивами.

    * table.insert(t, [pos,] value): вставляет элемент. * table.remove(t, [pos]): удаляет элемент. * table.sort(t, [comp]): сортирует массив. * table.concat(t, [sep]): объединяет элементы массива в строку.

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

    Библиотека Math: вычисления

    Здесь собраны стандартные тригонометрические функции, логарифмы, округление и генерация случайных чисел.

    Рассмотрим практический пример. Допустим, нам нужно найти расстояние между двумя точками на плоскости и . Для этого используется теорема Пифагора.

    Формула расстояния:

    Где: * — искомое расстояние (distance). * — координаты первой точки. * — координаты второй точки. * — операция извлечения квадратного корня.

    Реализация на Lua:

    Также полезны функции округления: * math.floor(x) — округление вниз. * math.ceil(x) — округление вверх. * math.random(n) — случайное целое число от 1 до .

    Библиотека OS и IO

    * IO (Input/Output): Работа с файлами. Есть две модели: простая (через io.input, io.output) и полная (через файловые дескрипторы).

    * OS (Operating System): Работа с датой, временем и системными вызовами. * os.time() — возвращает текущее время (обычно в секундах с начала эпохи Unix). * os.date() — форматирует дату в читаемый вид. * os.execute(cmd) — выполняет команду терминала.

    Основы C API

    Теперь мы переходим к «сердцу» Lua. Почему Lua называют встраиваемым языком? Потому что он спроектирован так, чтобы жить внутри программы на C/C++. Весь интерпретатор Lua — это библиотека C, которую вы подключаете к своему проекту.

    C API — это набор функций языка C, которые позволяют:

  • Вызывать Lua-код из C.
  • Вызывать C-код из Lua.
  • Обмениваться данными между ними.
  • Виртуальный Стек (The Virtual Stack)

    Главная проблема при взаимодействии C и Lua — это управление памятью. В Lua есть автоматическая сборка мусора (Garbage Collector), а в C вы должны управлять памятью вручную. Кроме того, Lua использует динамическую типизацию, а C — статическую.

    Чтобы решить эти противоречия, Lua использует абстрактный стек.

    !Визуализация стека обмена данными между C и Lua.

    Когда C хочет передать значение в Lua, он кладет его в стек. Когда C хочет получить значение из Lua, он забирает его из стека.

    #### Индексация стека

    У каждого элемента в стеке есть индекс. Это критически важно понимать.

  • Положительные индексы () отсчитываются от дна стека (первый добавленный элемент).
  • Отрицательные индексы () отсчитываются от вершины стека (последний добавленный элемент).
  • Индекс всегда указывает на самый верхний элемент. Это удобно, когда мы не знаем, сколько всего элементов в стеке, но хотим взять последний.

    Пример: Сложение чисел на C для Lua

    Представьте, что мы хотим написать функцию на C, которая складывает два числа, и вызвать её из Lua скрипта. Назовем её l_add.

    Алгоритм работы функции на C:

  • Получить первый аргумент из стека (индекс 1).
  • Получить второй аргумент из стека (индекс 2).
  • Сложить их.
  • Положить результат на вершину стека.
  • Вернуть количество результатов (в нашем случае 1).
  • Примерный псевдокод на C (с использованием Lua API):

    После регистрации этой функции в Lua, мы сможем писать так:

    Зачем это нужно?

    Вы можете спросить: «Зачем писать на C, если можно сложить числа в Lua?». Сложение — простой пример. В реальности C API используется для:

  • Оптимизации: Тяжелые математические расчеты (физика, обработка изображений) на C работают в десятки раз быстрее.
  • Доступа к железу: Lua не умеет напрямую работать с видеокартой или сетевой картой. C умеет. Вы пишете драйвер на C, а управляете им из Lua.
  • Использования сторонних библиотек: Хотите использовать мощную библиотеку машинного обучения, написанную на C++? Просто напишите для неё обертку (binding) через C API и используйте в Lua.
  • Заключение

    Сегодня мы значительно расширили горизонты. Мы узнали, что Lua — это не замкнутая система. Через стандартные библиотеки вы можете управлять файлами и текстом, а через C API — взаимодействовать с внешним миром и выжимать из компьютера максимальную производительность.

    Это знание завершает блок основ. Теперь вы понимаете, как язык устроен изнутри и как он общается с внешним миром. Впереди вас ждет практика и создание более сложных архитектурных решений.