Написать пост

5 фишек Lua, которые позволяют оставаться ему популярным

Аватарка пользователя Ирина Тюльпакова

IT-канал Awesome на примерах рассказал, почему язык Lua остаётся востребованным. Некоторые могут сломать вам голову, если с Lua вы до этого не сталкивались.

Lua — маленький скриптовый язык. Как правило, он идёт в связке с каким-то дополнительным языком, например C или C#, и часто используется в геймдеве. К тому же, Lua — очень быстрый язык, на нём пишут модули, чтобы ускорить, например, тот же Python.

IT-канал Awesome подробно рассказал об интересных особенностях Lua.

Превью видео f5MpjDtnFP4

В видео рассказывают:

  1. Что из себя представляет Lua.
  2. Как выглядит базовый синтаксис языка.
  3. Таблица — единственная структура данных, которая прекрасно обходится в одиночку.
  4. Типов данных в Lua всего 8.
  5. Как выглядит поток управления в Lua.
  6. Стандартные библиотеки и API позволяют работать с другими языками, например с С.

Ниже — транскрибация ролика.

Если вы взглянете на диаграммы, показывающие самый быстрорастущий язык программирования в проектах с открытым исходным кодом, вас ждет большой сюрприз. Во-вторых, внимательно изучив Rust, вы обнаружите крошечный язык под названием Lua. Так в чем же дело? Почему Lua вдруг стал таким популярным и, что более важно, есть ли польза в его изучении? Давайте потратим несколько минут на исследование основных функций Lua и преимуществ, и к концу <...> вы увидите, что благодаря своей простоте и популярности Lua может стать ценным дополнением к вашему набору навыков. 

Для краткости: я разработчик программного обеспечения с 15-летним опытом работы, в основном на языках, основанных на JVM в бэкенде и различных библиотеках пользовательского интерфейса во фронтенде. Мне нравится исследовать то, что предлагает мир разработки, и я создал довольно простую и эффективную систему для оценки жизнеспособности языка программирования.

Как выглядит программирование на Lua

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

Итак, Lua эффективен и легковесен, поскольку он опирается всего на 22 зарезервированных ключевых слова и 8 базовых типов. Еще более интересным, однако, является его подход к структурам данных. Здесь нет классов, структур, интерфейсов или трейтов. Нет массивов, списков или хэш-карт. 

Единственным механизмом структурирования данных, доступным в Lua, является таблица. На первый взгляд это может показаться странным, но через секунду вы увидите, что эта базовая структура достаточно универсальна и мощна, чтобы удовлетворить все ваши потребности. Благодаря своей легковесности и низкому барьеру входа, Lua также можно легко встраивать в другие хост-клиенты, начиная от популярных игр, таких как Roblox, и заканчивая современными IDE, такими как NeoVim. Итак, давайте перейдем к самой интересной части и рассмотрим некоторый код.

Мы рассмотрим 7 основных языковых функций, о которых вам необходимо знать, чтобы ориентироваться в языке. Сначала в списке давайте посмотрим, как на самом деле выглядит базовая программа на Lua.

			local user = {
  name = "",
  age = 20,
}

		

Мы определяем локальную пользовательскую переменную, указывающую на ссылку на таблицу. Поскольку мы можем использовать строковые имена полей в качестве индексов, это заставит таблицу вести себя как запись (record) или словарь.

			function user.hello()

  print("My name is" .. user.name .. "and I`m" .. user.age)

  end
		

В Lua функции являются значениями первого класса (first-class values), поэтому мы можем легко присоединять методы к нашему пользовательскому объекту. Язык довольно гибкий, и функция user.hello также может быть определена непосредственно в таблице.

			local user = {

name = "",

age = 20,

hello = function (self) --например так

  print("My name is" .. user.name .. "and I`m" .. user.age)

  end

}
		

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

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

			io.write("Name: ")

user.name = io.read()

io.write("Age: ")

user.age = tonumber(io.read())
		

Наконец, мы убедимся, что длина имени больше нуля, используя другой интересный оператор, и вызовем user.hello, если проверка пройдет успешно. Мы можем запустить этот код в терминале, вызвав команду lua, за которой следует имя файла: 

			lua main.lua
		

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

Типы данных в Lua

В Lua существует восемь типов:

			local name = “John” --строка

local height = 6.6 --числа

local enable = true --булевская переменная

local user = nil --nil

local log = function () --функция

local coroutine = coroutine.create(action) --трейты

--пользовательские данные
		

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

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

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

Поток управления Lua

Далее давайте кратко рассмотрим поток управления Lua. Здесь все немного более подробно, но, тем не менее, просто. У вас есть доступ к вашим обычным операторам if-then-else, циклам while, а также к циклам repeat until, которые выполнят ваш код хотя бы один раз. Оператор for может быть числовым или общим, чтобы вы могли выполнять итерацию по значениям таблицы. 

Краткое примечание: я знаю, что синтаксис Lua может вам не подойти, особенно если вы имеете опыт работы с языками, которые фокусируются на лаконичной семантике.

Однако имейте в виду, что мы говорим о языке 30-летней давности, и тенденции тогда были немного другими. 

Таблица в Lua

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

			local table = {}
		

а затем просто добавить поля, используя один из этих подходов:

			table[“name”] = “John”

table.age = 2
		

Затем мы можем получить доступ к значениям полей либо с помощью точечной записи, либо с помощью квадратных скобок.

			print(table.name)

print(table[“age”])
		

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

Присваивание, передача параметров и возврат функций всегда выполняются по ссылке, поэтому всегда следите за тем, как вы изменяете свои данные. Для сравнения, примитивные типы Lua неизменяемы и всегда передаются по значению. Сейчас самое подходящее время кратко обсудить автоматическое управление памятью Lua. Благодаря сборщику мусора память Lua обрабатывается автоматически, поэтому вам не нужно беспокоиться о выделении или освобождении памяти для ваших объектов.

Метатаблицы в Lua

Метатаблицы - это еще одна интересная функция, направленная на повышение гибкости таблиц. Самый простой способ понять их - на примере. Представьте, что у нас есть простой пользовательский объект, который выводится на экран.

			local user = {
	first = "John",
	last = "Doe""
}
print(user)

		

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

			local metaUser = {

	return user.first .. “ “ .. user last

	end

}

setmetatable(user, metaUser) --привязка поведения

print(user)
		

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

			local user = {

local metaUser = {

	__tostring = function(user)

	return user.first .. " " .. user.last

	end,

	__call = function(user) --определили call-метод

}

setmetatable(user, metaUser)

print(user)

user() --здесь таблица вызывается как функция
		

Стандартные библиотеки Lua

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

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

			function task1()
  for i=1, 5 do
    print("Task 1, step" .. i)
    coroutine. yield()
  end
end

function task2()
  for i=1, 5 do
    print("Task 2, step" .. i)
    coroutine. yield()
  end
end

local co1 = coroutine.create(task1)
local co2 = coroutine.create(task2)

		

Lua и С

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

			#include <lua.h> 
#include <lauxlib.h> //библиотека с доступом к Lua API
#include <lualib.h>

typedef struct {
int x;
int y;

} Point;

static int new_point(lua_State +L) {
double x = luaL_checknumber(L, 1);
double y = luaL_checknumber(L, 2);

Point *p = (Point *)lua_newuserdata(L, sizeof(Point));
p->x = x;
p->y = yi
return 1;

int luaopen_mylib(lua_State x*L) {
lua_register(L, "new_point", new_point);
return Q;

		

В С-файле давайте убедимся, что у нас есть доступ к Lua API, а затем определим структуру Point. Функция new_point() позволяет нам создать экземпляр point, проверив данные, полученные из стека, и убедившись, что зарегистрировано новое значение пользовательских данных. Наконец, мы зарегистрируем функцию new_point() с помощью Lua, скомпилируем наш C-код, и мы должны быть готовы к работе. 

			require(“lib”)

local p = newPoint(10, 20)
		

В новом Lua-файле теперь мы можем загрузить наш C-модуль и напрямую использовать предоставленные функции и структуры данных. 

Экосистема Lua

Наконец, давайте оценим экосистему Lua, чтобы понять, достаточно ли сторонней поддержки и необходимых библиотек, которые мы можем использовать, чтобы не изобретать велосипед заново. Lua предлагает простую модульную систему для упаковки кода и легкого обмена им, особенно благодаря менеджеру пакетов Luarox. Существуют различные успешные библиотеки, начиная от хорошо зарекомендовавших себя веб-фреймворков, таких как Lapis, и заканчивая серверами веб-приложений, такими как OpenResty. Сообщество также живо и вовлечено, и вы увидите различные эксперименты, такие как перенос языка шаблонов JSX на Lua или Moonscript, динамический язык сценариев, который компилируется в Lua.

Следите за новыми постами
Следите за новыми постами по любимым темам
361 открытий2К показов