Введение в язык Lua

Lua - скриптовый язык программирования с динамической типизацией и автоматическим управлением памятью, отличающийся простотой дизайна и легкостью обучения. Благодаря своей простоте и встраиваемости, используется во многих играх и приложениях. Также язык прекрасно подходит для непрофессиональных программистов из-за простого синтаксиса. Имеет эффективные средства для взаимодействия с языком C, что позволяет совместить мощь языка C и простоту языка Lua. Язык поддерживает небольшое количество встроенных типов данных, а комбинированные типы данных представлены одной структурой - таблицей.

Типы данных

Тип Описание
nil Обозначает отсутствие значения
boolean Логический тип данных. Возможные значения: true (истина), false (ложь)
number Числовой тип данных, соответствует типу данных double (вещественные числа) в других языках. Пример: 3.5, 12
string Строковый тип данных(массив символов). Пример: "Hello world"
function Функция. В Lua - полноправный объект(функцию можно передавать как параметр, возвращать и т.д.)
userdata Пользовательские данные, полученные обычно из кода на другом языке(С/С++)
table Основная комбинированная структура данных языка Lua. Пример: tbl = {"Hello world", 2}

Переменные

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

x = 50 -- глобальная переменная
local var1, var2 = 10, 20 --локальные переменные(множественное присваивание)

Локальные переменные ограничены областью видимости блока(это может быть тело цикла, функции или модуля), в котором они объявлены.

Операторы

Оператор это символ, благодаря которому совершается манипуляция над операндами.

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

Для примера предположим, что имеется несколько переменных: a=10 b=30 c=25

Оператор Описание Пример
+ Сложение. a + b = 40
- Вычитание. a - b = -20
* Умножение. a * b = 300
/ Деление. b / a = 3
% Модуль(остаток от целочисленного деления). c % a = 5 --(c = 2*a + 5)
^ Экспонента. a ^ 2 = 100
- Унарный минус. -a = -10

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

Для примера предположим, что имеются две переменные: a=10 b=30

Оператор Описание Пример
== Сравнение двух операндов на равенство. a == b --> false
~= Сравнение двух операндов на неравенство. a ~= b --> true
> Сравнение двух операндов на больше. a > b --> false
< Сравнение двух операндов на меньше. a < b --> true
>= Сравнение двух операндов на больше или равно. a >= b --> false
<= Сравнение двух операндов на меньше или равно. a <= b --> true

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

Логические операторы, как и контролирующие структуры считают nil и false как false``(ложь), а все остальное как ``true (истина). Для примера предположим, что имеются переменные: a=true b=false с=nil

Оператор Описание Пример
and Логическое «И». Если первый операнд false, то возвращает его. В противном случае возвращает второй операнд.

a and b --> b

с and b --> с

or Логическое «Или». Если первый операнд true, то возвращает его. В противном случае возвращает второй операнд.

a or b --> a

с or b --> b

not Логическое «Нет». Если операнд true, то возвращает false, и наоборот

not a --> false

not c --> true

Остальные операторы

Также поддерживаются еще два оператора: конкатенации и длины

Оператор Описание Пример
.. Конкатенация(сложение) двух строк "Hello world" .. "!" --> "Hello world!"
# Длина строки или кол-во элементов в таблице #"Hello" --> 5

Контролирующие структуры

Циклы

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

Цикл Описание Пример
while Выполняет инструкции пока заданное условие является истинным. Перед выполнением проверяет условие на истинность.
condition = true
while condition do
    condition = someFunc()
end
Числовой for Обладает следующим синтаксисим: for var=exp1,exp2,exp3 do ... end, где exp1 - начальное значение, exp2 - конечное значение, exp3 - шаг. exp3 является необязательным(по умолчанию равно 1)
for i=1, 10 do print(i) end

for i=20, 5, -1 do print(i) end
Общий for Позволяет пройтись по элементам, полученным от итерирующей функции(например, ipairs).
tbl = {1, 3, 5, 7, 9}
for element in ipairs(tbl) do
    print(element)
end
repeat Выполняет инструкции пока заданное условие является истинным. Проверяет условие на истинность после выполнения(т.е. выполнится как минимум один раз).
repeat
    condition = someFunc()
until condition

Также циклы могут быть вложены друг в друга:

for i=1,3 do
    for j=10,20 do
        print(j)
    end
end

Инструкции break и return могут быть использованы для прерывания цикла. break прерывает цикл, в то время как return сразу же возвращает значение функции и завершает ее:

for i=0,10 do
    if i == 6 then
        print(i)
        break
    end
end

Условия

Инструкция if проверяет условие на истину, и выполняет последующие инструкции в случае истины. В случае неуспешной проверки(условие ложно), выполняется else часть(если она присутствует). Если необходимо несколько else условий то их удобнее заменить elseif инструкциями, без необходимости закрывать каждую инструкцию с помощью end:

if a > 0 then print("Positive") end

if b > 0 then
    print("Positive")
elseif b < 0 then
    print("Negative")
elseif b == 0 then
    print("Zero")
else
    print("Not a number")
end

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

if a > 0 then
    if b > 0 then
        someFunc()
    else
        otherFunc()
    end
else
    a = b
end

Функции

Функция это группа инструкций, которые выполняют какую-либо задачу. В Lua есть некоторое количество встроенных функций(например, print). Можно написать свою функцию, объявив ее следующим способом:

local function functionName(arg1, arg2, arg3, ..., argn)
    value = ...
    ...
    return value
end

local - необязательная часть(функция будет локальной в случае такого объявления, т.е. доступной в рамках блока, в котором она объявлена). functionName - имя нашей функции. arg1, arg2, arg3, ..., argn - аргументы функции, перечисленные через запятую ... - тело нашей функции, которое содержит все инструкции, которые необходимо выполнить. return value - значение, которое функция возвращает. Является необязательным: если не указано, то подразумевается, что функция возвращает nil. end - конец блока функции.

Для примера напишем функцию, которая складывает два числа:

function addNumbers(a, b)
    return a + b
end

print(addNumbers(1,3)) --> 4

Еще пример - нахождение максимума из двух чисел:

function max(num1, num2)
    return num1 > num2 and num1 or num2 -- если num1 больше, то условие true и возвращается num1, в противном случае num2
end

print(max(1,5)) --> 5

Функция может принимать переменное количество аргументов, для этого укажите «…» в качестве аргумента функции. Напишем функцию, которая ищет максимум переменного количества чисел:

function max(...)
    local args = {...} -- массив аргументов

    if #args == 0 then return end -- если нет аргументов, то не выполняем инструкции ниже

    local result = args[1] -- максимум инициализируем первым элементом массива

    for i, num in ipairs(args) do
        if num > result then
            result = num
        end
    end

    return result
end

print(max(1,15,2,5,6,24,12)) --> 24

Также функция может возвращать несколько значений:

function findByCondition(array, condition)
    for idx, value in ipairs(array) do
        if condition(value) then
            return idx, value
        end
    end
end

local idx, value = findByCondition({1, -5, 12}, function(value) return value > 10 end) -- передаем функцию в качестве параметра
print("Value ", value, " found at index ", idx)

Таблицы

Таблица это составной объект, который представляет собой множество пар «ключ-значение». Ключами могут быть значения любых типов, кроме nil. Значения могут быть представлены любым типом, в том числе nil (что равносильно удалению пары из таблицы). Таблицы - очень гибкий и мощный инструмент языка, т.к. с их помощью можно создать любой пользовательский тип данных, будь то массив, структура, множество и т.д:

myTable = {} -- инициализация пустой таблицы
myTable[1] = "Hello " -- элемент с целым индексом
myTable[2] = "world"
myTable["third"] = "!" -- элемент со строковым индексом

myTable[1] = nil -- удаление элемента из таблицы

otherTable = {"First", "Second", "Third"} -- инициализация таблицы с элементами

otherTable[4] = "Fourth"

pet = {
    name = "cat",
    weight = 5
} -- структура

print(pet.name) --> cat (обращение к элементу структуры)

Существет несколько полезных методов для работы с таблицами. Первый это table.insert(tbl, pos, el), который в качестве параметров принимает таблицу, позицию для вставки(опционально) и элемент, который необходимо вставить. Если позиция не указана, то элемент будет вставлен в конец. Второй это table.remove(tbl, pos), который удаляет элемент в таблице по указанному индексу, перестраивая при этом индексы.

Примечание

Если таблица содержит ключи не с целочисленным индексом, то индекс вставленного элемента будет целым числом равным последнему целочисленному индексу + 1

Важно

Индексы в языке Lua начинаются с 1, а не с 0 как в других языках программирования

Получить размер таблицы можно с помощью оператора #:

print(#tbl)

Важно

Размер таблицы определяется верно если все ее индексы целочисленные и последовательные, т.е. нет дыр. К примеру, если мы вручную удалим элемент из таблицы следующим образом tbl[2] = nil, то размер будет считаться неверно. Также размер будет считаться неверно, если в таблице присутствуют элементы не с целочисленными индексами, а, например, со строковыми. Получить размер такой таблицы можно, только пройдя по всем ее элементам циклом for с помощью итерирующей функции pairs

Итерирующие функции

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

tbl = {}
tbl["pet"] = "dog"
tbl["age"] = 4

for k, v in pairs(tbl) do
    print(k, " ", v) --> "pet dog", "age 4"

tbl = {}
table.insert(tbl, 1)
table.insert(tbl, 2)
table.insert(tbl, 3)

for k, v in ipairs(tbl) do
    print(k, " ", v) --> "1 1", "2 2", "3 3"
end

Примечание

Элементы, которые возвращает функция ipairs последовательны, т.е. начинаются с индекса 1, затем идет 2 и т.д. Функция pairs возвращает же элементы в произвольном порядке. Также если в таблице с целочисленными индексами имеются дыры(пропуск некоторых индексов), то функция ipairs вернет элементы с индексами до первой такой дыры, а последующие пропустит.

Объекты

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

local tree = {
    height = 0,
    grow = function(self) self.height = self.height + 1 end,
    show = function(self) print(self.height) end
}

tree:grow()
tree:show() --> 1
tree:grow()
tree:show() --> 2

В данном примере self это переменная, указывающая на сам объект, а синтаксис с : позволяет передать этот объект первым параметром в функцию, иначе бы пришлось использовать следующий синтаксис: tree.show(tree).