Lua - это легкий, высокоуровневый, мультипарадигменный язык программирования с динамической типизацией и автоматическим управлением памятью. Он был разработан в начале 1990-х годов в Католическом университете Рио-де-Жанейро (Бразилия) и изначально предназначался для встраивания в другие программы как скриптовый язык.
С самого начала Lua показался мне совсем невыразительным языком, можно сказать, что в первое время я считал его “игрушечным”. В свете того, что мне нужно было переписывать конфигурацию для Neovim — мне пришлось посмотреть как работать с этим языком и хотя бы немного подучить его. После того как я написал конфигурацию для редактора, я решил также написать шаблон конфигурации. Сам того не замечая, мне начал все больше нравиться этот язык и моё мнение о нем изменилось.
Где используется Lua?
- Lua популярен как встроенный язык сценариев для других языков и платформ, таких как C/C++, Java, Python. Его встраиваемость позволяет расширять функциональность приложений без перекомпиляции основного кода.
- Lua широко используется для написания сценариев (скриптов) в компьютерных играх. Он управляет игровой логикой, искусственным интеллектом, диалогами, анимацией и другими аспектами поведения объектов.
- Lua применяется в веб-фреймворках (например, Corona SDK, Marmalade SDK) и популярных веб-приложениях (WordPress, OpenCart, Joomla) для добавления динамических функций;
- Lua используется для создания интерфейсов и расширения функциональности программ, например, в графическом редакторе Adobe Lightroom.
- Благодаря компактности Lua применяется в портативных устройствах, например, в графических калькуляторах Texas Instruments TI-Nspire CX.
В общем-то, Lua применяется в разных областях и справляется с совершенно разными задачами благодаря его простоте, портативности, легкости встраиваемости и скорости выполнения.
Среда выполнения
Lua — интерпретируемый язык программирования, поэтому нам нужен интерпретатор Lua, чтобы запускать код. Мы можем установить его с помощью пакетного менеджера:
# MacOS
brew install lua
# Ubuntu
sudo apt install lua5.3
# Fedora/RHEL
sudo dnf install lua
# Arch Linux
sudo pacman -S lua
# Windows
choco install lua53
# Unix-подобные системы
asdf plugin add lua
asdf install lua latest
Чтобы запустить интерпретатор Lua в терминале, достаточно ввести команду lua
.
Основы синтаксиса
Если бы можно было описать синтаксис Lua в двух словах, то я описал бы его как “внебрачный сын Bash и Python”. Разработчики Lua пытались сделать язык простым для того чтобы люди, которые не знакомы или мало знакомы с программированием смогли на нем писать, отсюда и болячки, такие как “индексы начинаются с единицы” и локальные переменные объявляются с помощью ключевого слова local
, вместо какого-нибудь краткого let
или var
.
Мы начнем изучение с основ и доберемся до пакетного менеджера, так что впереди много всего интересного.
Комментарии
Комментарии в Lua начинаются с символа --
и продолжаются до конца строки. Многострочные комментарии начинаются с --[[
и заканчиваются на ]]
:
-- Однострочный комментарий
--[[
Многострочный комментарий
Он может занимать несколько строк
]]
Переменные
Переменные в Lua не имеют явной типизации и бывают двух типов: локальные и глобальные. Локальные переменные объявляются с помощью ключевого слова local
, тогда как глобальные – объявляются без него:
local foo = 'Обычная строчная переменная'
PI = 3.1415
В примере вверху foo
является локальное переменной и ограничивается областью видимости скоупа или файла. PI
— глобальная переменная, она будет доступна на протяжении всего выполнения программы из любого файла.
По умолчанию в Lua нет стандарта по именованию переменных, однако, стандартная библиотека написана с использованием snake_case
и UPPER_SNAKE_CASE
, поэтому нам тоже стоит придерживаться этого соглашения по именованию переменных и сущностей.
Константы
До версии Lua 5.6 в языке не существовало такого понятия, как константа, однако, в версии 5.6 появилась аннотация <const>
для создания констант, вот как выглядит данная конструкция:
local PI <const> = 3.1415
Типы данных
Как уже было сказано, в Lua нет строгой типизации, и переменные могут принимать значения разных типов. Однако, в Lua есть несколько базовых типов данных, которые мы должны держать в голове при работе с переменными и их значениями:
Тип данных | Описание |
---|---|
nil | Специальный тип, который обозначает отсутствие значения. Он используется для инициализации переменных и обозначения пустых значений. |
boolean | Логический тип, который может принимать значения true или false . |
number | Числовой тип, который представляет собой числа с плавающей запятой двойной точности (64 бита). Lua не различает целые и вещественные числа, все они представляются как number . |
string | Строковый тип, который представляет собой последовательность символов. Строки в Lua неизменяемы. |
function | Тип, представляющий функции. Lua поддерживает функции как объекты первого класса, что позволяет передавать их как аргументы и возвращать из других функций. |
table | Ассоциативный массив, который может содержать значения любого типа. Таблицы в Lua являются основным способом организации данных и представляют собой универсальную структуру данных. |
thread | Тип, представляющий легковесные потоки выполнения. Lua поддерживает многопоточность с помощью легковесных потоков, которые позволяют выполнять несколько задач одновременно. |
userdata | Тип, представляющий произвольные данные, которые могут быть созданы и использованы в Lua. Он позволяет взаимодействовать с данными, созданными на стороне C или других языков. |
Числа, строки и булевы значения
Числа в Lua представляют собой числа с плавающей запятой двойной точности (64 бита). Любое число в Lua будет размерностью в 64 бита, вне зависимости от того содержит оно плавающую точку или нет, это сделано для упрощения обращения с числами. Давайте рассмотрим пример:
local foo = 10 -- Целочисленное число // number
local bar = 10.2 -- Дробное число // тоже number
print(foo + bar) -- 20.2
Строки в Lua представляют собой последовательности символов и могут быть заключены в одинарные или двойные кавычки. Строки в Lua неизменяемы, что означает, что мы не можем изменять их содержимое после создания. Однако, мы можем создавать новые строки на основе существующих:
local str1 = 'Hello'
local str2 = "World"
local str3 = str1 .. ' ' .. str2 -- Конкатенация строк
print(str3) -- Hello World
Конкатенация строк в Lua осуществляется с помощью оператора ..
, который объединяет две строки в одну. Важно помнить, что строки в Lua неизменяемы, поэтому конкатенация создает новую строку, а не изменяет существующую.
Если вы примените оператор ..
к числам, Lua автоматически преобразует их в строки:
print(10 .. 25) -- 1025
Булевы значения в Lua представлены значениями true
и false
. Да и в целом, в Lua любое значение, кроме nil
и false
, считается истинным. Это означает, что даже числа и строки могут использоваться в логических выражениях.
Также, стоит отметить что в Lua нет оператора !
(логическое отрицание), вместо него используется оператор not
, который возвращает true
, если значение ложно, и false
, если значение истинно.
local a = 10
local b = 'Hello'
local c = true
local d = false
local e = 0
local f = nil
--[[
Мы будем использовать двойное отрицание, для того чтобы выяснить
к какому логическому значению приводится переменная
]]
print("a истинно? " .. not not a) -- true
print("b истинно? " .. not not b) -- true
print("c истинно? " .. not not c) -- true
print("d истинно? " .. not not d) -- false
print("e истинно? " .. not not e) -- false
print("f истинно? " .. not not f) -- false
Таблицы
Одним из интересных типов данных является таблица. Данный тип данных представляет собой ассоциативный массив, который может содержать значения любого типа. Таблицы в Lua являются основным способом организации данных и представляют собой универсальную структуру данных, которая совмещает в себе функции и массивов, и объектов:
local foo = { "Один", "Два", "Три" } -- Массив
local bar = { username = "tokiory", age = 22 } -- Ассоциативный массив (объект)
Более того, мы можем инициализировать массив и объект в одной таблице:
local info = { "tokiory", 22, site = "https://tokiory.vercel.app" }
Для того чтобы обратиться к элементу таблицы, мы можем использовать квадратные скобки или точечную нотацию:
local info = { "tokiory", 22, site = "https://tokiory.vercel.app" }
print(info[1]) -- tokiory
print(info[2]) -- 22
print(info.site) -- https://tokiory.vercel.app
Да, индексы в Lua начинаются с единицы. Придется смириться с этим, так как язык был ориентирован на людей, которые не знакомы с программированием. В Lua нет возможности изменить это поведение, так как оно является частью языка.
Мы также можем явно указать индекс, когда инициализируем таблицу:
local info = {
[1] = "tokiory",
[2] = 22,
site = "https://tokiory.vercel.app"
}
print(info[1]) -- tokiory
print(info[2]) -- 22
print(info.site) -- https://tokiory.vercel.app
Если же мы попробуем обратиться к элементу, которого нет в таблице, то Lua вернет nil
:
local foo = {}
print(foo[1]) -- nil
Для того чтобы получить длину таблицы, мы можем использовать оператор #
, который возвращает количество элементов в таблице:
local foo = { "Один", "Два", "Три" }
print(#foo) -- 3
Управление потоком выполнения
В Lua реализованы все базовые конструкции управления потоком выполнения, среди которых:
- Условия:
if
,elseif
,else
- Циклы:
for
,while
,repeat
- Переходы:
break
,goto
- Функции:
function
,return
Условия
Давайте начнем с условий, вот короткий пример применения конструкции if
/elseif
/else
:
local a = 100
if a > 0 then
print('a больше нуля')
elseif a < 0 then
print('a меньше нуля')
else
print('a равно нулю')
end
Примечательно, что Lua в данном случае совмещает синтаксис из Bash с его then
и синтаксис из Python с его отсутствием фигурных и круглых скобок для ограничения блока кода. В Lua также есть конструкция elseif
, которая позволяет проверять несколько условий подряд.
Циклы
Циклы в Lua реализованы с помощью трёх конструкций:
while
— выполняет блок кода, пока условие истинно.repeat
— выполняет блок кода хотя бы один раз, а затем проверяет условие.for
— выполняет блок кода заданное количество раз или итерируется по таблице.
Цикл while
Для начала давайте рассмотрим самый простой цикл — while
. Данная форма записи цикла позволяет выполнять блок кода до тех пор, пока условие истинно:
-- Пример бесконечного цикла
while true do
print('Это бесконечный цикл')
end
-- Пример цикла с условием
local a = 10
while a > 0 do
print(a)
a = a - 1 -- уменьшаем значение a на 1
end
Цикл repeat
Теперь давайте рассмотрим repeat
, он выполняет блок кода хотя бы один раз, а затем проверяет условие, в других языках (например, как Javascript) данный цикл известен под названием do while
. Если условие истинно, цикл продолжается:
-- Пример бесконечного цикла
repeat
print('Это бесконечный цикл')
until false
-- Пример цикла с условием
local a = 10
repeat
print(a)
a = a - 1 -- уменьшаем значение a на 1
until a == 0 -- цикл продолжается, пока a не станет равным 0
Цикл for
Ну, и наконец, давайте рассмотрим самый продвинутый цикл — for
.
Цикл for
в Lua позволяет задавать начальное значение, конечное значение и шаг. Если шаг не указан, он по умолчанию равен 1:
-- Перечисление чисел от 1 до 10 (с шагом по умолчанию — 1)
for i = 1, 10 do
print(i)
end
-- Перечисление чисел от 10 до 1 (с шагом в -2)
for i = 10, 1, -2 do
print(i)
end
Итерация по таблицам
В Lua также есть возможность итерироваться по таблицам с помощью функции pairs
или ipairs
. Функция pairs
позволяет итерироваться по всем элементам таблицы, а ipairs
— только по числовым индексам:
Стоит отметить, что ipairs
будет проходиться по массиву до тех пор, пока не найдет первый nil
, в то время как pairs
будет проходиться по всем элементам таблицы:
local foo = {
"tokiory",
22,
site = "https://tokiory.vercel.app",
"bar",
nil
"foo"
}
for index, value in ipairs(foo) do
print(index, value)
end
--[[
1 tokiory
2 22
3 "bar"
]]
-- (4 nil) и (5 "foo") не выведутся, так как ipairs обнаружил nil в качестве значения
Функции
Функции в Lua являются объектами первого класса, что означает, что мы можем передавать их как аргументы, возвращать из других функций и хранить в переменных. Функции могут быть определены с помощью ключевого слова function
, а затем вызваны по имени:
-- Определение функции
local function greet(name)
print("Hello, " .. name .. "!")
end
-- Вызов функции
greet("tokiory") -- Hello, tokiory!
Глобальные и локальные функции
Функции в Lua могут быть глобальными или локальными. Глобальные функции объявляются без ключевого слова local
, тогда как локальные функции объявляются с ним:
function global_func()
end
local function local_func()
end
Замыкания
Замыкание — это механизм позволяющий функции использовать контекст, в котором она была создана.
Это означает, что функция может использовать переменные из внешней области видимости даже после того, как эта область видимости была завершена:
local createGreeter = function(greeting)
-- Данная функция может использовать параметр greeting из внешней функции
return function(name)
print(greeting .. ", " .. name .. "!")
end
end
local greetHello = createGreeter("Hello")
local greetHi = greetHello("Daniil") -- Hello, Daniil!
Коллбэки
Коллбэки — это функции, которые передаются в качестве аргумента другим функциям и вызываются внутри них. Это позволяет создавать более гибкие и настраиваемые функции:
local printer = function(message)
print(message)
end
local functionWithCallback = function(callback)
callback("Hello, World!")
end
functionWithCallback(printer)
-- Hello, World!
Варидические функции
Варидические функции — это функции, которые могут принимать переменное количество аргументов. В Lua это достигается с помощью ...
, который представляет собой список аргументов:
local function add(...)
local sum = 0
for _, value in ipairs({...}) do
sum = sum + value
end
return sum
end
print(add(1,2,3,4,5)) -- 15
Также, функции в Lua могут возвращать сколько угодно значений, используя оператор return
.
Если функция не возвращает значения, то она вернет nil
:
local function nillish()
end
nillish() -- nil
local function foo()
return 1, 2, 3 -- Возвращаем кортеж значений
end
local a, b, c = foo() -- a = 1, b = 2, c = 3
Вариадические функции и кортежи
Синтаксис {...}
позволяет разворачивать список аргументов в таблицу, что позволяет нам использовать функции ipairs
и pairs
для итерации по ним. Это полезно, когда мы хотим передать переменное количество аргументов в другую функцию или обработать их как массив.
Важно понимать, что если в аргументах есть nil
, то мы не сможем использовать ipairs
, так как он остановится на первом nil
.
Для того чтобы справляться с кейсами, где в аргументах есть nil
была придумана функция select
.
Данная функция позволяет нам выбирать элементы из списка аргументов, начиная с определенного индекса. Она принимает два аргумента: индекс и список аргументов.
Индекс может быть положительным или отрицательным. Если индекс положительный, то функция вернет все элементы, начиная с этого индекса. Если индекс отрицательный, то функция вернет все элементы, кроме тех, которые находятся до этого индекса:
local function getFromSecond(...) {
return select(2, ...)
}
local function getLast(...)
return select(-1, ...)
}
local a, b, c = getFromSecond(1, 2, 3, 4, 5) -- a = 2, b = 3, c = 4
local d = getLast(1, 2, 3, 4, 5) -- d = 5
В случае использования getFromSecond
мы на самом деле получили четыре значения, но присвоили их только в три переменные, поэтому c
будет равен 4
, а 5
будет проигнорирован.
В случае с getLast
мы получили только одно значение, которое присвоили переменной d
.
Также, с помощью select
мы можем получить количество аргументов, переданных в функцию, используя #
:
local function getCount(...)
return select('#', ...)
end
local count = getCount(1, 2, 3, 4, 5) -- count = 5
Для того чтобы обойти ограничение с nil
и ipairs
нам достаточно перебрать аргументы с помощью select
:
local function printArgs(...)
for i = 1, select('#', ...) do -- Перебираем все индексы кортежа
-- Возвращаем все значения кортежа начиная с i,
-- но игнорируем все остальные значения, кроме первого, который вернулся
local element = select(i, ...)
print(element)
end
end
Методы
В Lua функции могут быть определены как методы, которые могут быть вызваны на объектах.
Методы — это функции, которые определены внутри таблицы и могут использоваться для работы с данными этой таблицы.
Мы можем определить методы двумя способами, первый из них является более распространенным и используется чаще:
local person = {
name = "tokiory",
age = 22,
-- Инициализируем метод
greet = function(self)
print("Hello, my name is " .. self.name .. " and I'm " .. self.age .. " years old.")
end
}
Мы можем вызвать метод, используя точечную нотацию:
person.greet() -- Hello, my name is tokiory and I'm 22 years old.
Вторым методом является использование оператора :
, который позволяет нам передать объект в качестве первого аргумента функции автоматически. Это позволяет нам не передавать объект вручную, а использовать его как параметр с названием self
внутри метода:
local person = {
name = "tokiory",
age = 22,
}
-- Инициализируем метод
function person:greet()
print("Hello, my name is " .. self.name .. " and I'm " .. self.age .. " years old.")
end
-- Вызов метода
person:greet() -- Hello, my name is tokiory and I'm 22 years old.
Метатаблицы и ООП
Метатаблицы — это специальные таблицы, которые позволяют изменять поведение других таблиц. Они позволяют переопределять стандартные операции, такие как сложение, вычитание, сравнение и другие.
Метатаблицы используются для создания пользовательских типов данных и реализации объектно-ориентированного программирования. Они отличаются от обычных таблиц тем, что у них есть метаметоды, именно они и позволяют изменять поведение таблиц.
Метаметоды — это функции, которые вызываются при выполнении определенных операций над таблицами. Например, метаметод __add
вызывается при сложении двух таблиц, а __index
— при обращении к элементу таблицы.
Для того чтобы использовать метатаблицы нам нужно создать таблицу, в которой будут эти самые методы:
-- mt часто используется как сокращение от "metatable"
local mt = {
__add = function(a, b)
return a.value + b.value
end,
}
Далее нам понадобятся специальные функции для установки и получения метатаблицы, они называются setmetatable
и getmetatable
соответственно:
setmetatable(table, metatable)
— устанавливает метатаблицу для таблицы.getmetatable(table)
— возвращает метатаблицу для таблицы.
Мы можем использовать их с нашим mt
для того чтобы добавить возможность складывать таблицы:
local mt = {
__add = function(a, b)
return a.value + b.value
end,
}
local foo = { value = 10 }
local bar = { value = 20 }
setmetatable(foo, mt)
setmetatable(bar, mt)
local result = foo + bar
print(result) -- 30
В Lua доступны следующие метаметоды:
Метаметод | Вызывается при | Описание |
---|---|---|
__index | t[k] (отсутствующий ключ) | Настраивает доступ к полям таблицы (наследование прототипов). |
__newindex | t[k] = v (отсутствующий ключ) | Настраивает присвоение значения отсутствующим ключам. |
__add | a + b | Настраивает поведение при сложении. |
__sub | a - b | Настраивает поведение при вычитании. |
__mul | a * b | Настраивает поведение при умножении. |
__div | a / b | Настраивает поведение при делении. |
__mod | a % b | Настраивает поведение при вычислении остатка от деления. |
__pow | a ^ b | Настраивает поведение при возведении в степень. |
__unm | -a | Настраивает поведение при унарном минусе. |
__concat | a .. b | Настраивает поведение при конкатенации. |
__eq | a == b | Настраивает поведение при сравнении на равенство. |
__lt | a < b | Настраивает поведение при сравнении на меньше. |
__le | a <= b | Настраивает поведение при сравнении на меньше или равно. |
__call | t() | Делает таблицу вызываемой как функцию. |
__tostring | tostring(t) или print(t) | Настраивает преобразование таблицы в строку. |
__len | #t | Настраивает поведение оператора длины. |
__gc | Сборка мусора | Настраивает поведение таблиц при сборке мусора. |
__metatable | Получение метатаблицы | Настраивает поведение таблиц при получении метатаблицы. |
__pairs | Итерация через pairs | Настраивает поведение таблиц при итерации по таблице с помощью pairs . |
__ipairs | Итерация через ipairs | Настраивает поведение таблиц при итерации по таблице с помощью ipairs . |
__mode | Использование слабых ссылок | Настраивает поведение таблиц при использовании слабых ссылок. |
__name | Использование функции type | Настраивает поведение таблиц при использовании функции type . |
Метаметоды во многом похожи на прототипы в JavaScript, но в Lua они реализованы через метатаблицы. Это позволяет создавать более сложные структуры данных и реализовывать объектно-ориентированное программирование.
ООП
В Lua нет встроенной поддержки объектно-ориентированного программирования, но мы можем реализовать его с помощью метатаблиц и функций. Мы можем создать класс, который будет представлять собой таблицу, а методы будут представлять собой функции, которые работают с этой таблицей.
Давайте создадим класс Person
, который будет представлять собой человека с именем и возрастом:
local Person = {}
-- Конструктор класса
function Person:new(name, age)
local obj = { name = name, age = age }
setmetatable(obj, self)
self.__index = self
return obj
end
-- Метод класса
function Person:greet()
print("Hello, my name is " .. self.name .. " and I'm " .. self.age .. " years old.")
end
-- Создаем экземпляр класса
local person1 = Person:new("tokiory", 22)
person1:greet() -- Hello, my name is tokiory and I'm 22 years old.
Теперь мы можем более подробно разобрать данный сниппет кода:
Метатаблицы строк
После того, как мы выучили как работать с функциями и поняли как работают методы — самое время посмотреть как работать с методами уже встроенными в язык для работы со строками.
В Lua для работы со строками есть глобальная таблица string
, от которой наследуются все строки (хотя, наверное, корректней говорить, что таблица string
является метатаблицей для всех строк).
Вот какие функции реализованы в таблице string
:
Функция | Описание |
---|---|
string.byte | Возвращает числовое значение ASCII символа в строке по указанной позиции. Если указаны начальная и конечная позиции, возвращает значения для всех символов в этом диапазоне. |
string.char | Преобразует один или несколько числовых кодов ASCII в строку. |
string.find | Ищет подстроку в строке и возвращает начальную и конечную позиции первого совпадения. Также может возвращать nil , если подстрока не найдена. |
string.format | Форматирует строку, используя синтаксис, похожий на printf в C. |
string.gmatch | Возвращает итератор, который позволяет проходить по всем совпадениям шаблона в строке. |
string.gsub | Заменяет все вхождения шаблона в строке на указанную подстроку или результат функции. |
string.len | Возвращает длину строки. |
string.lower | Преобразует все символы строки в нижний регистр. |
string.match | Ищет первое совпадение шаблона в строке и возвращает его. |
string.rep | Повторяет строку указанное количество раз и возвращает результат. |
string.reverse | Возвращает строку в обратном порядке. |
string.sub | Возвращает подстроку, начиная с указанной позиции и заканчивая другой указанной позицией. |
string.upper | Преобразует все символы строки в верхний регистр. |
string.pack | Упаковывает значения в бинарную строку в соответствии с указанным форматом. |
string.unpack | Распаковывает значения из бинарной строки в соответствии с указанным форматом. |
string.packsize | Возвращает размер строки, необходимой для хранения данных в указанном формате. |
К примеру, давайте возьмем метод lower
, который позволяет преобразовать строку в нижний регистр. Мы можем использовать его передав в него строку или же использовать оператор :
для того, чтобы вызвать его как метод и передать саму строку в качестве аргумента self
:
local foo = "HELLO"
print(string.lower(foo)) -- hello
print(foo:lower()) -- hello
Модули
В Lua модули представляют собой таблицы, которые содержат функции и данные, которые могут быть использованы в других частях программы. Модули позволяют организовать код и разделить его на логические части.
Для создания модуля нам нужно создать новый файл, в которым мы инициализируем таблицу и вернем её в конце файла:
-- Общепринятной практикой считается называть модули буквой "M"
local M = {}
-- Инициализируем функции внутри модуля
M.hello = function()
print("Hello, World!")
end
return M
Для того чтобы использовать модуль, нам нужно его импортировать с помощью функции require
:
local mod = require("mod")
mod.hello() -- Hello, World!
Проверка на выполнение файла
В случае, если у вас есть модуль, который может являться исполняемым файлом, мы можем использоваться конструкцию if ... == nil
для того, чтобы проверить, является ли файл исполняемым:
local M = {}
-- Инициализируем функции внутри модуля
M.hello = function()
print("Hello, World!")
end
-- Проверяем, является ли файл исполняемым
if ... == nil then
M.hello()
end
return M
Встроенные модули
Ранее мы говорили о работе со строками и о том, что у строк есть метатаблица string
. О работе с теми же числами мы не говорили ни слова, потому что в Lua нет встроенной метатаблицы для работы с числами, но есть встроенный модуль math
, который позволяет работать с числами и математическими функциями.
Вот список функций, которые реализованы в модуле math
:
Функция | Описание |
---|---|
math.abs | Возвращает абсолютное значение числа. |
math.acos | Возвращает арккосинус числа в радианах. |
math.asin | Возвращает арксинус числа в радианах. |
math.atan | Возвращает арктангенс числа в радианах. |
math.atan2 | Возвращает арктангенс координат y, x в радианах. |
math.ceil | Округляет число вверх до ближайшего целого. |
math.cos | Возвращает косинус угла в радианах. |
math.cosh | Возвращает гиперболический косинус числа. |
math.deg | Преобразует угол из радиан в градусы. |
math.exp | Возвращает экспоненту числа (e^x). |
math.floor | Округляет число вниз до ближайшего целого. |
math.fmod | Возвращает остаток от деления двух чисел. |
math.huge | Представляет бесконечность. |
math.log | Возвращает натуральный логарифм числа. |
math.log10 | Возвращает десятичный логарифм числа. |
math.max | Возвращает максимальное значение из списка аргументов. |
math.min | Возвращает минимальное значение из списка аргументов. |
math.modf | Возвращает дробную и целую части числа. |
math.pi | Константа, представляющая число π (пи). |
math.pow | Возводит число в степень (эквивалент x^y ). |
math.rad | Преобразует угол из градусов в радианы. |
math.random | Возвращает случайное число. |
math.randomseed | Устанавливает начальное значение для генератора случайных чисел. |
math.sin | Возвращает синус угла в радианах. |
math.sinh | Возвращает гиперболический синус числа. |
math.sqrt | Возвращает квадратный корень числа. |
math.tan | Возвращает тангенс угла в радианах. |
math.tanh | Возвращает гиперболический тангенс числа. |
math.type | Возвращает тип числа ("integer" или "float" ). |
math.ult | Возвращает true , если первое число меньше второго беззнаковым сравнением. |
Для того чтобы работать со встроенными модулями — нам не нужно их импортировать, так как они уже доступны в глобальной области видимости:
print(math.pow(2,3)) -- 2^3 = 8
Среди других встроенных модулей также есть следующие:
Модуль | Описание |
---|---|
coroutine | Предоставляет функции для работы с сопрограммами, такими как создание, возобновление и остановка сопрограмм. |
io | Предоставляет функции для работы с вводом-выводом, включая чтение и запись файлов. |
os | Предоставляет функции для работы с операционной системой, такие как получение текущего времени, выполнение команд и управление процессами. |
package | Предоставляет функции для работы с модулями и пакетами, включая загрузку и управление путями поиска модулей. |
table | Предоставляет функции для работы с таблицами, такие как сортировка, вставка и удаление элементов. |
utf8 | Предоставляет функции для работы с UTF-8 строками, включая проверку валидности и получение длины строки. |
debug | Предоставляет функции для отладки, такие как получение информации о вызовах функций и управление метатаблицами. |
LuaRocks
LuaRocks — это менеджер пакетов для Lua, который позволяет устанавливать модули из репозиториев и управлять модулями и библиотеками, а также создавать свои собственные модули и делиться ими с другими пользователями. Он поддерживает различные версии Lua, что позволяет использовать его с различными проектами и библиотеками.
Для того чтобы установить LuaRocks, вам нужно скачать его с официального сайта и следовать инструкциям по установке. После установки вы сможете использовать команду luarocks
в терминале для управления пакетами:
Корутины
Корутины — это легковесные потоки, которые позволяют выполнять несколько задач одновременно в одном потоке. Они позволяют приостанавливать выполнение функции и возобновлять его позже, что позволяет создавать асинхронные операции и обрабатывать события.
Для того чтобы использовать корутины в Lua есть отдельный встроенный модуль coroutine
, который предоставляет функции для создания, управления и взаимодействия с корутинами:
Функция | Назначение |
---|---|
coroutine.create(f) | Создает корутину на основе функции f |
coroutine.resume(co [, ...]) | Запускает/возобновляет выполнение корутины |
coroutine.yield(...) | Приостанавливает выполнение |
coroutine.status(co) | Возвращает статус: "suspended" , "running" , "dead" |
coroutine.wrap(f) | Возвращает функцию, обертку над корутиной |
coroutine.running() | Возвращает текущую корутину (если есть) |
Давайте создадим маленькую корутину, которая будет асинхронно отдавать числа:
local range = coroutine.create(function(n)
for i = 1, n do
coroutine.yield(i)
end
end)
-- Запускаем корутину
coroutine.resume(range) -- 1
coroutine.resume(range) -- 2
coroutine.resume(range) -- 3
coroutine.resume(range) -- 4
--- ...
Мы также можем ожидать значения от корутины в цикле:
local range = coroutine.create(function(n)
for i = 1, n do
coroutine.yield(i)
end
end)
-- Запускаем корутину
for i in range(5) do
print(i)
end
Обработка ошибок
В Lua есть встроенные функции pcall
, xpcall
и error
, которые позволяют обрабатывать ошибки и создавать их.
Для начала рассмотрим создание ошибок, это делается с помощью встроенной функции error
, которая принимает любой тип данных в качестве аргумента и создает ошибку с этим значением. Она также может принимать второй аргумент, который указывает на уровень стека, на котором произошла ошибка.
error("Something went wrong!") -- Создаем ошибку
error({code = 408, message = "Server is deadass busy to work on your request"})
Функция pcall
(сокращение от “protected call” или же “защищенный вызов”) — это функция, которая позволяет вызывать функции в защищенном режиме. Она принимает функцию и ее аргументы, выполняет ее и возвращает статус выполнения и результат. Если функция завершилась с ошибкой, то pcall
вернет false
и сообщение об ошибке.
local function divide(a, b)
if b == 0 then
error("division by zero")
end
return a / b
end
local status, result = pcall(divide, 10, 0) -- Делим на ноль
print(status) -- false
print(result) -- division by zero
--[[
false
main.lua:3: division by zero
]]
Также, у нас есть функция xpcall
, которая работает точно также, как и pcall
, однако, она позволяет нам передать функцию-обработчик ошибок, которая будет вызвана в случае возникновения ошибки. Она принимает функцию без аргументов, а также функцию-обработчик ошибок, в случае если нам нужно обработать функцию с аргументами, то мы можем просто обернуть ее в анонимную функцию:
local function divide(a, b)
if b == 0 then
error("division by zero")
end
return a / b
end
local function error_handler(err)
print("WTF is this ->>> " .. err)
end
local status, result = xpcall(function() divide(10, 0) end, error_handler) -- Делим на ноль
print(status) -- false
print(result) -- division by zero
--[[
WTF is this ->>> main.lua:3: division by zero
false
nil
]]