Lua за 15 минут

Для всех, кто хотел разобраться с Lua (скриптовый язык для разработки игр и не только, список), но никак не находил времени, Tyler Neylon приготовил небольшой подарок:
Посвящается всем, кто предпочитает один большой список из говорящих самих за себя сниппетов кода (с небольшими комментариями к 95% case’ов) длинным мануалам с огромной иерархией. Очень удобно для тех, кто уже умеет программировать и просто хотел бы разобраться с новым для себя языком. Весь «мега-сниппет» на английском, но примеры несложно читаются.
P.S. А для всего остального есть Hyperpolyglot.
Lua: маленький язык, который смог
Lua — это, пожалуй, мой любимый «маленький язык», с низкой когнитивной нагрузкой и простотой в изучении и использовании. Он встроен во многое ПО, такое как Redis, NGINX через OpenResty и Wireshark (прим. перевод.: и многие другие). Он также используется в качестве скриптового языка в таких играх, как World of Warcraft и Roblox через Luau (прим. перевод.: и многих других). Этот пост — краткое признание в любви языку с некоторыми примерами того, почему он мне так нравится.

Простота
В Lua относительно немного фич и относительно мало синтаксиса. К примеру, в языке всего 8 типов:
- nil
- boolean
- number
- string
- userdata (для представления С-шных структур данных или блоков памяти в куче)
- function
- thread (для корутин)
- table (ассоциативный массив и, по совместительству, единственная встроенная структура данных)
Нет нужды беспокоиться о float , int , usize . Нет нужды беспокоиться о различиях в массивах, словарях и структурах. Даже классы тут это просто таблицы ( table ) с указанными мета-таблицами ( metatables , прим. перев.: думайте о прототипах, как в JavaScript). Такая простота во всём делает Lua лёгким в освоении и использовании, обеспечивая при этом достаточную мощь для выполнения большинства необходимых задач.
Давайте реализуем простой бинарный поиск по массиву на Lua:
-- однострочные комментарии начинаются с двух тире function binary_search(array, value) local low = 1 local high = #array -- # это оператор взятия длины while low value then high = mid - 1 else return mid end end return nil end res = binary_search(, 6) print(res)
Всё это должно быть относительно знакомо, даже если вы никогда раньше не сталкивались с Lua. Единственное, что может показаться непривычным, это ключевое слово local , которое используется для объявления переменных (прим. перев.: и не только). По умолчанию все они глобальны, так что local используется для объявления локальной переменной относительно текущей области видимости.
Транспиляция
Lua является великолепной целью для транспиляции, благодаря его простоте и лёгкости взаимодействия с C. Поэтому, Lua — отличный выбор для предметно-ориентированных языков (DSL-ей), таких как Terra, MoonScript и Fennel.
В качестве краткого примера, вот тот же бинарный поиск написанный в MoonScript и Fennel соответственно:
binary_search = (array, value) -> low = 1 high = #array while low value high = mid - 1 else return mid return nil print binary_search , 6
(fn binary-search [array value] (var low 1) (var high (length array)) (var ret nil) (while ( mid-value value) (set high (- mid 1)) (do (set ret mid) (set low high)))) ; no early returns in Fennel ret) (local res (binary-search [2 4 6 8 9] 6)) (print res)
Встраиваемость
Но истинная сила языка заключается в том, что вы можете внедрить его практически куда угодно — Lua реализован как библиотека для программы-хоста, типа Redis. Традиционно, это была программа на C, но теперь существуют многие реализации виртуальной машины Lua в разных языках, таких как Javascript (с Fengari) или Go (c GopherLua). Однако, одной из самых популярных реализаций является скриптовый язык Luau для игры Roblox.
Возможно, одно из моих любимых применений Lua — это HAProxy, возвращающий нас во времена Apache + mod_php скриптинга. Давайте настроим конфигурацию HAProxy, которая будет отвечать на запросы по определённому пути случайным предсказанием:
local function fortune(applet) local responses = < < quote = "The only people who never fail are those who never try.", author = "Ilka Chase" >, < quote = "The mind that is anxious about future events is miserable.", author = "Seneca" >, < quote = "A leader is a dealer in hope.", author = "Napoleon Bonaparte" >, < quote = "Do not wait to strike until the iron is hot; but make it hot by striking.", author = "William B. Sprague" >, < quote = "You have power over your mind - not outside events. Realize this, and you will find strength.", author = "Marcus Aurelius" >> local response = responses[math.random(#responses)] local resp = string.format([[ %s
--%s
]], response.quote, response.author) applet:set_status(200) applet:add_header("content-length", string.len(resp)) applet:add_header("content-type", "text/html") applet:start_response() applet:send(resp) end core.register_service("fortune", "http", fortune)
global lua-load fortune.lua defaults retries 3 timeout http-request 10s timeout queue 1m timeout connect 10s timeout client 1m timeout server 1m timeout http-keep-alive 10s timeout check 10s frontend fe_main bind :8080 mode http http-request use-service lua.fortune if < path /fortune >
И затем запустим её:
$ haproxy -f haproxy.cfg
$ curl localhost:8080/fortune Do not wait to strike until the iron is hot; but make it hot by striking.
--William B. Sprague
Зачем вообще так делать? Легко вообразить ситуацию, в которой нужна небольшая логика приложения поверх веб-сервера, но вы не хотите писать полноценной веб-приложение под эту задачу. Или же вы хотите расширить функционал существующих приложений, например, добавить небольшой запрос (endpoint) для проверки статуса (healthcheck) Redis-сервера:
-- это сторонний форк redis-lua с поддержкой TLS: local redis = require("redis-tls") local settings = < host = "127.0.0.1", port = 6379, database = 14, password = nil, >local utils = < create_client = function(params) local client = redis.connect(params.host, params.port, 1, false) if params.password then client:auth(params.password) end return client end, >local function redis_health(applet) -- pcall как try/catch, принимает функцию и аргументы, -- и возвращает true/false и результат выполнения функции local ok, client = pcall(utils.create_client, settings) if not ok then local string_resp = '\n' applet:set_status(500) applet:add_header("content-length", string.len(string_resp)) applet:add_header("content-type", "application/json") applet:start_response() applet:send(string_resp) return end local resp = client:ping() local string_resp = string.format('\n', resp) applet:set_status(200) applet:add_header("content-length", string.len(string_resp)) applet:add_header("content-type", "application/json") applet:start_response() applet:send(string_resp) end core.register_service("redis_health", "http", redis_health)
global lua-load redis.lua frontend fe_main bind :8080 mode http http-request use-service lua.redis_health if < path /redis_health >
$ curl 127.0.0.1:8080/redis_health
Redis-сервер работает и доступен:
$ curl 127.0.0.1:8080/redis_health
Можно пойти дальше и использовать register_action и register_fetches (см. доки) для перехвата информации о запросе, его изменения или добавления дополнительных возможностей аутентификации поверх ПО без встроенной системы аутентификации.
Сообщество
Оно не особо велико, но в нём ведётся множество отличных разработок, а многие библиотеки доступны через простой пакетный менеджер LuaRocks. От библиотек для быстрого парсинга и кодировки JSON до дополнений к стандартной библиотеке (прим. перевод.: или даже добавления программируемых компьютеров в Minecraft) — для каждого найдётся что-то своё.
Отдельного упоминания заслуживает Лиф Коркоран (Leaf Corcoran), написавший Lapis — фантастический небольшой веб-фреймворк для дистрибутива OpenResty, на котором работает сайт LuaRocks.
Прим. перевод.: В отличие от некоторых сообществ, в Lua нет ни мелких, ни больших скандалов или интриг (см. Rust : ^1, ^2, ^3)
Заключение
Есть ли какой-то вывод? Lua очень хорош, вы можете освоить его за выходные и начать использовать его для написания слоёв авторизации в HAProxy, аддонов для World of Warcraft, игр в рамках Roblox, скриптов для вашего оконного менеджера, работы с сетями или просто небольших библиотек, которые делают вас чуточку счастливее.
Прим. перевод.: и не забывайте про игровые движки — Love2D, Defold и интеграция с Raylib.
От себя я бы рекомендовал посмотреть на Fennel, упомянутый выше в переводе. Это LISP, который транспилируется в Lua — вы получаете скорость, простоту и доступность Lua c гибкостью LISP-синтаксиса и макросов.
А для тех, кто только начинает свой путь в программировании, у меня есть промокод HABR23. Где применить не скажу, а то реклама 🙂
Кстати, я веду Телеграм-канал для заинтересованных в обучении и осмыслении всего в IT, присоединяйтесь, если ещё не!
Lua за 60 минут
Я сентиментальный программист. Иногда я влюбляюсь в языки программирования, и тогда я могу говорить о них часами. Одним из этих часов я поделюсь с вами.
Lua? Что это?
Lua — простой встраиваемый язык (его можно интегрировать с вашими программами, написанными на других языках), легкий и понятный, с одним типом данных, с однообразным синтаксисом. Идеальный язык для изучения.
Зачем?
Lua может вам пригодится:
* если вы геймер (плагины для World of Warcraft и множества других игр)
* если вы пишете игры (очень часто в играх движок пишут на C/C++, а AI — на Lua)
* если вы системный программист (на Lua можно писать плагины для nmap, wireshark, nginx и других утилит)
* если вы embedded-разработчик (Lua очень быстрый, компактный и требует очень мало ресурсов)
Что надо для того, чтобы читать дальше?
1. Научитесь программировать. Хотя бы немного. Не важно на каком языке.
2. Установите Lua. Для этого либо скачайте здесь версию 5.2 (http://www.lua.org/download.html), либо ищите ее в репозиториях. Версия 5.1 тоже пойдет, но знайте, что она очень старая.
Все примеры из статьи запускайте в терминале командой наподобие «lua file.lua».
Первые впечатления
Lua — язык с динамической типизацией (переменные получают типы «на лету» в зависимости от присвоенных значений). Писать на нем можно как в императивном, так и в объектно-ориентированном или функциональном стиле (даже если вы не знаете как это — ничего страшного, продолжайте читать). Вот Hello world на Lua:
-- my first lua app: hello.lua print "hello world"; print("goodbye world")
Что уже можно сказать о языке:
* однострочные комментарии начинаются с двух дефисов «—»
* скобки и точки-с-запятыми можно не писать
Операторы языка
Набор условных операторов и циклов довольно типичен:
-- условные операторы (ветки else может не быть) if a == 0 then print("a is zero") else print("a is not zero") end -- сокращенная форма if/elseif/end (вместо switch/case) if a == 0 then print("zero") elseif a == 1 then print("one") elseif a == 2 then print("two") else print("other") end -- цикл со счетчиком for i = 1, 10 do print(i) end -- цикл с предусловием b = 5 while b > 0 do b = b - 1 end -- цикл с постусловием repeat b = b + 1 until b >= 5
ПОДУМАЙТЕ: что может означать цикл «for i = 1, 10, 2 do . end» ?
В выражениях можно использовать такие вот операторы над переменными:
* присваивание: x = 0
* арифметические: +, -, *, /, % (остаток от деления), ^ (возведение в степень)
* логические: and, or, not
* сравнение: >, =, ~= (не-равно, да-да, вместо привычного «!=»)
* конкатенация строк (оператор «..»), напр.: s1=»hello»; s2=»world»; s3=s1..s2
* длина/размер (оператор #): s=»hello»; a = #s (‘a’ будет равно 5).
* получение элемента по индексу, напр.: s[2]
Битовых операций в языке долгое время не было, но в версии 5.2 появилась библиотека bit32, которая их реализует (как функции, не как операторы).
Типы данных
Я вам соврал, когда сказал что у языка один тип данных. Их у него много (как и у каждого серьезного языка):
* nil (ровным счетом ничего)
* булевы числа (true/false)
* числа (numbers) — без деления на целые/вещественные. Просто числа.
* строки — кстати, они очень похожи на строки в паскале
* функции — да, переменная может быть типа «функция»
* поток (thread)
* произвольные данные (userdata)
* таблица (table)
Если с первыми типами все понятно, то что же такое userdata? Вспомним о том, что Lua — язык встраиваемый, и обычно тесно работает с компонентами программ, написанными на других языках. Так вот, эти «чужие» компоненты могут создавать данные под свои нужды и хранить эти данные вместе с lua-объектами. Так вот, userdata — и есть подводная часть айсберга, которая с точки зрения языка lua не нужна, но и просто не обращать внимания на нее мы не можем.
А теперь самое важное в языке — таблицы.
Таблицы
Я вам снова соврал, когда сказал, что у языка 8 типов данных. Можете считать что он один: всё — это таблицы (это, кстати, тоже неправда). Таблица — это очень изящная структура данных, она сочетает в себе свойства массива, хэш-таблицы («ключ»-«значение»), структуры, объекта.
-- Итак, вот пример таблицы как массива: a = -- массив из 3-х элементов print(a[2]) -- выведет "2", потому что индесы считаются с единицы -- А таблица в виде разреженного массива (у которого есть не все элементы) a = <> -- пустая таблица a[1] = 1 a[3] = 5
ПОДУМАЙТЕ: чему равно a[2] в случае разреженного массива?
В примере выше таблица ведет себя как массив, но на самом деле — у нас ведь есть ключи (индексы) и значения (элементы массива). И при этом ключами могут быть какие угодно типы, не только числа:
a = <> a["hello"] = true a["world"] = false a[true] = 1 -- или так: a = < hello = 123, world = 456 >print(a["hello")) print(a.hello) -- то же самое, что и a["hello"], хотя выглядит как структура с полями
Кстати, раз уж у таблицы есть ключи и значения, то можно в цикле перебрать все ключи и соответствующие им значения:
t = < a = 3, b = 4 >for key, value in pairs(t) do print(key, value) -- выведет "a 3", потом "b 4" end
А как же объекты? О них мы узнаем чуть позже, вначале — о функциях.
Функции
Вот пример обычной функции.
function add(a, b) return a + b end print(add(5, 3)) -- напечатает "8"
Функции языка позволяют принимать несколько аргументов, и возвращать несколько аргументов. Так аргументы, значения которых не указаны явно, считаются равными nil.
ПОДУМАЙТЕ: зачем может понадобиться возвращать несколько аргументов?
function swap(a, b) return b, a end x, y = swap(x, y) -- кстати, это можно сделать и без функции: x, y = y, x -- и если уж функция возвращает несколько аргументов, -- а они вам не нужны - игнорируйте их с помощью -- специальной переменной-подчеркивания "_" a, _, _, d = some_function()
Функции могут принимать переменное количество аргументов:
-- в прототипе переменное число аргументов записывается как троеточие function sum(. ) s = 0 for _, n in pairs(arg) do -- в функции обращаются к ним, как к таблице "arg" s = s + n end return a end sum(1, 2, 3) -- вернет 6 sum(1, 2, 3, 4) -- вернет 10
Поскольку функции — это полноценный тип данных, то можно создавать переменные-функции, а можно передавать функции как аргументы других функций
a = function(x) return x * 2 end -- функция, умножающая на 2 b = function(x) return x + 1 end -- функция, увеличивающая на 1 function apply(table, f) result = <> for k, v in pairs(table) do result[k] = f(v) -- заменяем элемент на какую-то функцию от этого элемента end end -- ПОДУМАЙТЕ: что вернут вызовы t = apply(t, a) apply(t, b)
Объекты = функции + таблицы
Раз мы можем сохранять функции в переменных, то и в полях таблиц тоже сможем. А это уже получаются как-бы-методы. Для тех, кто не знаком с ООП скажу, что основная его польза (по крайней мере в Lua) в том, что функции и данные, с которыми они работают находятся рядом — в пределах одного объекта. Для тех, кто знаком с ООП скажу, что классов здесь нет, а наследование прототипное.
Перейдем к примерам. Есть у нас объект, скажем, лампочка. Она умеет гореть и не гореть. Ну а действия с ней можно сделать два — включить и выключить:
lamp = < on = false >function turn_on(l) l.on = true end function turn_off(l) l.on = false end -- это просто функции для работы со структурой turn_on(lamp) turn_off(lamp)
А если лампочку сделать объектом, и функции turn_off и turn_on сделать полями объекта, то получится:
lamp = < on = false turn_on = function(l) l.on = true end turn_off = function(l) l.on = false end >lamp.turn_on(lamp) lamp.turn_off(lamp)
Мы вынуждены передавать сам объект лампочки в качестве первого аргумента, потому что иначе наша функция не узнает с какой именно лампочкой надо работать, чтобы сменить состояние on/off. Но чтобы не быть многословными, в Lua есть сокращенная запись, которую обычно и используют — lamp:turn_on(). Итого, мы уже знаем несколько таких упрощений синтаксиса:
lamp:turn_on() -- самая общепринятая запись lamp.turn_on(lamp) -- то с точки зрения синтаксиса это тоже правильно lamp["turn_on"](lamp) -- и это
Продолжая говорить о сокращениях, функции можно описывать не только явно, как поля структуры, но и в более удобной форме:
lamp = < on = false >-- через точку, тогда аргумент надо указывать function lamp.turn_on(l) l.on = true end -- через двоеточкие, тогда аргумент неявно задается сам, как переменная "self" -- "self" - и есть та лампочка, для которой вызвали метод function lamp:turn_off() self.on = false end
Специальные функции
Некоторые имена функций таблиц (методов) зарезервированы, и они несут особый смысл:
* __add(a, b), __sub(a, b), __div(a, b), __mul(a, b), __mod(a, b), __pow(a, b) — вызываются, когда выполняются арифметические операции над таблицей
* __unm(a) — унарная операция «минус» (когда пишут что-то типа «x = -x»)
* __lt(a, b), __le(a, b), __eq(a, b) — вычисляют результат сравнения ( * __len(a) — вызывается, когда делается «#a»
* __concat(a, b) — вызывается при «a..b»
* __call(a, …) — вызывается при «a()». Переменные аргументы — это аргументы при вызове
* __index(a, i) — обращение к a[i], при условии, что такого элемента не существует
* __newindex(a, i, v) — создание «a[i] = v»
* __gc(a) — когда объект удаляется при сборке мусора
Подменяя эти методы, можно перегружать операторы и использовать синтаксис языка для своих целей. Главное не переусердствовать.
Наследование
Для тех, кто не знает ООП, наследование позволяет расширить функциональность уже существующего класса. Например, просто лампочка умеет включаться-выключаться, а супер-ламкочка будет еще и яркость менять. Зачем нам переписывать методы turn_on/turn_off, если можно их повторно использовать?
В Lua для этого есть понятие мета-таблицы, т.е. таблицы-предка. У каждой таблицы есть одна таблица-предок, и дочерняя таблица умеет делать все, что умеет предок.
Допустим, что объект-таблицу lamp мы уже создали. Тогда супер-лампочка будет выглядеть так:
superlamp = < brightness = 100 >-- указываем родительскую таблицу setmetatable(superlamp, lamp) -- и ее методы теперь доступны superlamp:turn_on() superlamp:turn_off()
Расширение функциональности
Родительские таблицы есть у многих типов (ну у строк и таблиц точно, у чисел и булевых чисел, и у nil их нет). Допустим, мы хотим складывать все строки с помощью оператора «+» , а не «..» . Для этого надо подменить функцию «+» (__add) для родительской таблицы всех строк:
s = getmetatable("") -- получили родительскую таблицу строки s.__add = function(s1, s2) return s1..s2 end -- подменили метод -- проверяем a = "hello" b = "world" print(a + b) -- напишет "helloworld"
Собственно, мы еще можем заменить функцию print с помощью «print = myfunction», да и много других хакерских дел можно сделать.
Области видимости
Переменные бывают глобальные и локальные. При создании все переменные в Lua являются глобальными.
Для указания локальной области видимости пишут ключевое слово local:
local x local var1, var2 = 5, 3
Не забывайте об этом слове.
Обработка ошибок
Часто, если возникают ошибки, надо прекратить выполнение определенной функции. Можно, конечно, сделать множество проверок и вызывать «return», если что-то пошло не так. Но это увеличит объем кода. В Lua используется что-то наподобие исключений (exceptions).
Ошибки порождаются с помощью функции error(x). В качестве аргумента можно передать все, что угодно (то, что имеет отношение к ошибке — строковое описание, числовой код, объект, с которым произошла ошибка и т.д.)
Обычно после этой функции вся программа аварийно завершается. А это надо далеко не всегда. Если вы вызываете функцию, которая может создать ошибку (или ее дочерние функции могут создать ошибку), то вызывайте ее безопасно, с помощью pcall():
function f(x, y) . if . then error("failed to do somthing") end . end status, err = pcall(f, x, y) -- f:функция, x-y: ее аргументы if not status then -- обработать ошибку err. В нашем случае в err находится текст ошибки end
Стандартные библиотеки
Стандартных библиотек мало, зато это позволяет запускать Lua где угодно. Подробнее можно получить их список здесь — http://www.lua.org/manual/5.2/manual.html
Нестандартных библиотек много, их можно найти на LuaForge, LuaRocks и в других репозиториях.
Между Lua и не-Lua
ВНИМАНИЕ: эту часть рекомендуется читать людям со знанием языка C.
А если нам недостаточно функциональности стандартных библиотек? Если у нас есть наша программа на C, а мы хотим вызывать ее функции из Lua? Для этого есть очень простой механизм.
Допустим, мы хотим создать свою функцию, которая возвращает случайное число (в Lua есть math.random(), но мы хотим поучиться). Нам придется написать вот такой код на C:
#include #include #include /* собственно, что делать при вызове `rand(from, to)` */ static int librand_rand(lua_State *L) < int from, to; int x; from = lua_tonumber(L, 1); /* первый параметр функции */ to = lua_tonumber(L, 2); /* второй параметр функции */ x = rand() % (to - from + 1) + from; lua_pushnumber(L, x); /* возвращаемое значение */ return 1; /* возвращаем только один аргумент */ >/* в Lua "rand" соответствует нашей функции librand_rand() */ static const luaL_reg R[] = < , /* конец списка экспортируемых функций */ >; /* вызывается при загрузке библиотеку */ LUALIB_API int luaopen_librand(lua_State *L) < luaL_openlib(L, "librand", R, 0); srand(time(NULL)); return 1; /* завершаемся успешно */ >
Т.е. Lua предоставляет нам функции для работы с типами данных, для получения аргументов функций и возврата результатов. Функций очень мало, и они довольно простые. Теперь мы собираем нашу библиотеку как динамическую, и можем использовать функцию rand():
random = require("librand") -- загружаем библиотеку print(random.rand(1, 100)) print(random.rand(0, 1))
А если мы хотим вызывать код, написанный на Lua из наших программ? Тогда наши программы должны создавать виртуальную машину Lua, в которой и будут выполняться Lua-скрипты. Это намного проще:
#include "lua.h" #include "lauxlib.h" int main() < lua_State *L = lua_open(); // создаем виртуальную машину Lua luaL_openlibs(L); // загружаем стандартные библиотеку luaL_dofile(L, "rand.lua"); // выполняем скрипт lua_close(L); // закрываем Lua return 0; >
Все.
Вы теперь можете писать на Lua. Если вы узнаете интересные моменты про Lua, которые можно было бы отразить в статье — пишите!
Язык программирования Lua: что это такое и где используется

Давайте об этом поподробнее. Действительно, в языке присутствует один главный тип данных — таблицы. Хотя на самом деле Lua понимает, что такое int, float и bool, но в общем случае они являются таблицами с каким-то определённым значением. Таблица — это аналог ассоциативного массива или структуры. К сожалению, аналога перечисления enum в языке нет, но эта структура данных может быть создана дополнительно.
Выбирая Lua в качестве языка программирования, следует помнить о его минималистичности. Создатели прежде всего старались сделать его компактным, небольшим, чтобы с его помощью можно было программировать на самых разных устройствах. Так что надо быть готовым к тому, что стандартных функций здесь будет не так уж и много. Но при этом всегда можно подключить библиотеку на C, где уж чего-чего, а реализуемых функций накопилось просто гигантское количество.
Таблицы здесь лежат в основе также объектно-ориентированного программирования, построения классов. Впрочем, это далеко не новая концепция, и во многих других языках всё очень похоже.
Вторая концепция, заложенная в язык — это то, что функции являются объектами первого порядка. Соответственно, язык полностью реализует лямбда-исчисление. Здесь можно производить развёртывание и свёртывание, другие операции, допускается многократная вложенность функций. Реализованы другие механизмы, callback, замыканиe, лямбда-абстракции. Можно писать эффективный код при помощи разных видов рекурсии. Будет ли это хорошим кодом — решать вам. В любом случае можно писать, используя таблицы и объекты. Но всё-таки Lua, по заверениям его авторов, является функциональным языком, и код, написанный на нём в этом стиле, будет эффективным.
Наконец, третья концепция языка — он по факту может считаться некоторой «надстройкой» над языком ANSI C. Данный язык запускается на всех устройствах, поддерживающих архитектуру процессора 8086. Он может быть использован при программировании устройств умного дома, программировании роботов, в том числе и промышленных, разного рода летательных аппаратов. И у этой концепции есть свои последствия.
Последняя особенность открывает перед Lua большие возможности. Lua — интерпретируемый язык. Однако по скорости исполнения программы с ним не сравнится никакой другой интерпретируемый язык. Ни JS, ни NodeJS, никакие виды лиспов, ни Python, ни Haskell не опережают Lua по скорости исполнения кода. Впрочем, тут всё зависит от программы, как она написана на том или ином языке — в общем случае скорость исполнения кода зависит больше от программиста. Наконец, код на Lua можно легко компилировать, чтобы он мог исполняться практически на любой платформе из бинарного файла. ANSI C поддерживается всеми известными устройствами, где есть что-то похожее на процессор, и Lua, соответственно, тоже может поддерживаться там.
Однако прямая свять с C накладывает на Lua и ряд ограничений. Во-первых, C — это самый простой способ выстрелить себе в ногу. Хотя в Lua и имеется сборщик мусора, всё-таки доступны функции и управления памятью по адресам. Странные преобразования типов и переполнение стека тоже могут иметь место. Но всё это справедливо для программ, где используется данная особенность. В обычных программах нужно просто знать особенности работы сборщика мусора.
Для каких целей используют язык Lua
Большинство людей, которые программируют на Lua, используют его для написания игр. Например, для создания пользовательских режимов в Dota 2, программирования аддонов для Starcraft. Lua — встраиваемый язык, и, пожалуй, сложно найти игру, где он бы не использовался для написания логики и сценария игры, или же для написания аддонов. Программируют на нём и собственно игры. В частности, мобильные приложения для сред Lowe2d, CoronaSDK и других. Простенькие игры могут создаваться за несколько часов, а язык программирования подарит им исключительное быстродействие и скорость загрузки. Чем, кстати, часто не могут похвастаться игры на Java.
Используется Lua и для написания читов к играм. Здесь можно легко реализовать быструю смену оружия, компенсацию отдачи при выстреле, автонаведение на цель и просто бота, который будет играть вместо игрока. Lua может эффективно обрабатывать информацию с экрана, быстро анализировать её и находить ответ, даже не используя какие-то дополнительные «хакерские» интерфейсы взаимодействия с игрой. Скорость работы позволяет анализировать картинку напрямую и обрабатывать её в режиме реального времени.
Вторая область применения — это то, благодаря чему Lua стал языком читеров. Искусственный интеллект, который может принимать решения за игрока, обрабатывать и распознавать образы с экрана — вот то, что позволяет это делать. Один из самых крупных, самых хорошо документированных фреймворков для ИИ, Torch, был написан на Lua. С его помощью можно писать для него и расширения. Библиотеки анализа картинки, которые были изначально написаны для Torch, активнее всего используются программистами-читерами.
Некоторые почему-то считают, что лучшим языком для реализации нейронных сетей и построения ИИ является Python. Но попробуйте с помощью Python проанализировать какой-то видеопоток в режиме реального времени! А на Lua это вполне себе обычная задача. Особенно с использованием C-модулей. PyTorch — лишь форк фреймворка Torch, реализованный с помощью того, для он чего изначально не создавался.
Далее, встраиваемые системы. Lua может использоваться для программирования микроконтроллеров. И используется. С его помощью реализуются как студенческие проекты, так и коммерческие, например, управление квадрокоптером или автоматом полива теплиц.
Наконец, последняя область — это для системного администрирования, программирования интерфейсов и сокетов. Ещё совсем недавно для подобных целей особенно широко использовался Perl. Однако время идёт, программистов на Perl становится всё меньше. А вот программистов на Lua достаточно много. Всё благодаря играм, которые его используют.
Вообще, наблюдается странная тенденция — язык программирования, на котором не пишут игры, имеет все шансы быть забытым. На Perl практически никогда не писали игры — и сейчас он почти забыт всеми.
Поэтому для подобных задач Lua будет, пожалуй, более перспективным языком, чем Perl. Особенно учитывая его быстродействие и возможность подключения модулей из C, а также включения программ на нём в C-код.
Синтаксис языка и среды разработки
Для Lua характерен несколько избыточный паскалеподобный синтаксис, где нужно указывать слово end. Слово begin в качестве операторной скобки не исползуют, вместо него здесь слово do. Нет здесь также и привычного многим оператора switch/match — придётся привыкать писать конструкцию else, elseif. Логические операции тоже придётся писать словами. В остальном же всё очень похоже на язык Javascript с той лишь разницей, что в последнем функции не есть объекты первого порядка, и переменные обычно являются ссылками, а в Lua можно работать с областью памяти напрямую. Классы здесь реализованы с помощью таблиц и метатаблиц, что ещё больше роднит Lua с языком JS. Такое «неполноценное» ООП, тем не менее, достаточно удобно для быстрой разработки приложений.
Для тех, кто решил всерьёз заняться изучением Lua, можно порекомендовать различные среды. Почти все известные IDE поддерживают разработку на этом языке. Однако автор всё-таки рекомендует для изучения обратиться к специализированной IDE ZeroBrainStudio, а не использовать привычный VScode или что-то ещё, более привычное. Дело в том, что для таких сфер применения Lua, как программирование игр, здесь есть удобные преднастройки, которые учитывают ограничения для Lua в различных играх. Есть очень понятный и удобный встроенный отладчик, «заточенный» именно под Lua. И есть также встроенные разнообразные библиотеки на этом языке, которые можно подключать сразу здесь, не скачивая извне.
Недостатки языка
Главный недостаток — это то, что Lua разрабатывался и развивался как скриптовый язык в основном. Поэтому реализовывать на нём большие проекты было бы не слишком хорошей идеей. Впрочем, здесь всё, опять же, зависит от организации разработки. При желании разработка крупного проекта будет не сложнее, чем на языке Python. Учитывая то, что язык в общем-то не предназначен для больших программ, не стоит расстраиваться по этому поводу.
Второй недостаток является следствием минималистичного синтаксиса на Lua. Многие функции придётся создавать с нуля самому, например, конструкцию для continue. Знание библиотек и умение их искать будет необходимым условием.
Достоинства языка
Кроме уже выше сказанного о том, что Lua, пожалуй, самый быстрый скриптовый интерпретируемый язык, сюда следует записать простоту освоения. Минималистский набор слов языка позволяет запомнить его конструкции за пару часов и уже начать писать программы. При этом использовать всю мощь Lua, такую, как ООП и работу с таблицами. Набор стандартных встроенных функций не так уж и велик, чтобы их пришлось изучать долго — тоже будет достаточно пары недель.
Подойдёт ли Lua для новичков
Многим кажется, что Lua подойдёт для новичков в качестве первого языка программирования. Раз имеет простой и понятный синтаксис, является интерпретатором по умолчанию, а также всего двадцать одно зарезервированное слово (для сравнения, в том же Python их более 50). Действительно, для того, чтобы заинтересовать ребёнка программированием, это очень хороший вариант. Тем более, если он поймёт, как на нём можно писать различные аддоны для GTA и Minecraft.
Однако является ли он простым языком? Нет и ещё раз нет! Особенности управления памятью, а также полноценная реализация ФП необходимы для использования всей мощи языка. И это будет совсем не просто. Lua можно изучать первым языком лишь в случае, когда есть хороший наставник, давно программирующий на нём. Который сможет объяснить все концепции языка и программирования вообще. Для сравнения, по той же причине не стоит выбирать в качестве первого ЯП Javascript. За кажущейся простотой и удобством использования в браузере кроются особенности, которые делают программирование на нём сложным.
Lua, безусловно, более сложный язык, чем JS. Lua прежде всего функциональный язык, и писать на нём хорошие программы проще всего в функциональном стиле. Изучать же этот стиль лучше совсем не на JS и не на Lua. Для изучения функционального программирования подойдёт Scheme и книга SICP, «библия» программирования. Или же какая-то литература по языку Haskell.
Кому же подойдёт Lua? Если не брать разработку аддонов для игр, где часто используется урезанная, упрощённая версия Lua, то для изучения этого языка нужно как минимум немного знать программирование на C. Кроме этого, полезным будет умение работать на Javascript, языке, у которого многие конструкции реализованы точно так же. Словом, это может быть программист с уровнями знания выше начального.
Что делать при самостоятельном изучении
При самостоятельном изучении легко упереться в отсутствие большого количества литературы, как, например, для языка PHP или Java. Однако есть большое количество форумов, достаточно обширное сообщество, удовлетворительное количество бесплатных видеокурсов. Самый лучший вариант — для начала освоить какой-нибудь один из многочисленных курсов типа «Lua за час», чтобы войти в разработку на этом языке легко, и потом потихоньку пользоваться сайтом lua.org, официальным ресурсом, который поможет улучшить обрубочные знания, полученные таким способом. А для начальной сферы изучения лучше всего выбрать как раз разработку игр, ну а потом перейти на что-то другое.
Стоит отговорить неопытных разработчиков от того, чтобы использовать книгу Роберту Иерузалимского как учебник для новичков. Это немного другая книга, пособие по языку, а не учебник. Автор писал её с таким расчётом, что читатель уже знаком с первоначальными понятиями, такими, как объект, класс и метод, ссылка и ряд других, умеет писать программы. Её можно использовать как второй учебник, где достаточно красиво и ярко показаны все особенности языка, а также имеется большое количество очень интересных упражнений, примеров.
Заключение
Язык программирования Lua можно рекомендовать изучать в первую очередь тем, кто хочет программировать игровые дополнения, аддоны, сценарии. Эта область применения не потребует каких-то особенных познаний языка Lua, не слишком требовательна к знаниям по программированию в целом. Часто для игр используют урезанные версии языка. Для более опытных программистов с его помощью можно будет создавать приложения для нейросетей, API, управления сокетами, автоматизации задач администрирования. Наконец, для микроконтроллеров всегда можно использовать Lua, и это тоже очень удобно. Lua является сложным языком, однако порог вхождения в него достаточно низок. Специализированная IDE для Lua — ZeroBrainStudio, также написанная на Lua.