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

Краткая справка

Lua бы придуман в 1993 году в Католическом университете Рио-де-Жанейро. Название переводится с португальского, как Луна, причем создатели убедительно просят не писать LUA, чтобы, не дай Бог, кто-нибудь не принял название за аббревиатуру. Является мультипарадигмальным скриптовым языком, использующим прототипную модель ООП.

Типизация здесь динамическая, а для реализации наследования используются метатаблицы, то есть это прекрасный инструмент для расширений возможностей вашего продукта. Причем из-за своей компактности он пригоден для использования практически на любой платформе. Посудите сами: tarball Lua 5.3.4 весит всего 296 килобайт (в “разжатом” виде — 1.1 мегабайт), интерпретатор (написанный на C) для Linux — от 182 до 246 килобайт, а стандартный набор библиотек — ещё 421 килобайт.

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

Начнем с традиционного:

Согласитесь, знакомо и не слишком информативно. Более интересный пример с точки зрения знакомства с Lua — вычисление факториала введенного числа:

function fact (n)
if n == 0 then
return 1
else
return n * fact(n-1)
end
end

print("enter a number:")
a = io.read("*number") — read a number
print(fact(a))

Все предельно понятно. Кстати, в Lua поддерживается параллельное присваивание:

И в заключении довольно простой пример с использованием библиотек:

#include
#include
#include
#include
#include

int main (void) <
char buff[256];
int error;
lua_State *L = lua_open(); /* opens Lua */
luaopen_base(L); /* opens the basic library */
luaopen_table(L); /* opens the table library */
luaopen_io(L); /* opens the I/O library */
luaopen_string(L); /* opens the string lib. */
luaopen_math(L); /* opens the math lib. */

while (fgets(buff, sizeof(buff), stdin) != NULL) <
error = luaL_loadbuffer(L, buff, strlen(buff), "line") ||
lua_pcall(L, 0, 0, 0);
if (error) <
fprintf(stderr, "%s", lua_tostring(L, -1));
lua_pop(L, 1); /* pop error message from the stack */
>
>

Преимущества и недостатки

Итак, чем же хорош Lua?

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

Во-вторых, он быстрый. Если взглянуть на сравнительную характеристику с другими языками, то можно заметить, что хоть Lua и не укладывает на лопатки C или Python, но в некоторых тестах показывает отличные результаты.

В-третьих, он очень удобен для изучения даже не самыми опытными программистами. Наверняка даже те, кто просто знает английский язык, поняли минимум 80% описанного выше кода и без проблем смогут воспроизвести.

Четвертый аргумент — у вас не возникнет никаких проблем с поиском информации. У Lua есть много полезной информации непосредственно на официальном сайте. Кроме того, очень недурно развито сообщество на StackOverFlow и IRC-чате, а в США разработчики и создатели так и вовсе встречаются каждый год.

Любителей современных тенденций привлечет тот факт, что на Lua можно писать функциональный код.Да что там, если вы хотя бы пару недель уделите программированию на Lua, то наверняка найдёте ещё не один десяток плюсов.

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

Хотите стать веб-разработчиком? Тогда вам на наш курс обучения web-разработке!

Среды разработки

LDT (Lua Development Tools) для Eclipse — расширение для одной из наиболее популярных IDE;

ZeroBrane Studio — специализированная среда, написанная на Lua;

Decoda — не самая популярная кроссплатформенная IDE, но в качестве альтернативы подойдет;

SciTE — хороший редактор, полноценно поддерживающий Lua;

WoWUIDesigner — угадайте, для какой игры эта среда помогает обрабатывать скрипты, в том числе на Lua?

Полезные ссылки

http://www.lua.org/home.html — официальный сайт со всей необходимой информацией, учебником, книгами, документацией и даже есть немного специфического юмора;

http://tylerneylon.com/a/learn-lua/ — отличная обучалка от Tyler Neylon. Подойдет программистам с опытом, кто хорошо знает английский язык (впрочем, со словарем тоже не возникнет больших проблем) и просто желает расширить свой кругозор;

https://zserge.wordpress.com/2012/02/23/lua-за-60-минут/ — основы Lua за 60 минут от явно неравнодушного к этому языку программиста. На русском языке;

https://youtube.com/watch?v=yI41OL0-DWM — видеоуроки на YouTube, которые помогут вам наглядно разобраться с настройкой IDE и базовыми принципами языка.

Наш сегодняшний гость — настоящий боец скрытого фронта. Вы могли видеть его в играх (World of Warcraft, Angry Birds, X-Plane, S.T.A.L.K.E.R.) или продуктах компании Adobe (Lightroom), но даже не задумывались о его существовании. Между тем этому языку уже почти 25 лет и всё это время он незаметно делал нашу виртуальную жизнь чуть лучше.

Краткая справка

Lua бы придуман в 1993 году в Католическом университете Рио-де-Жанейро. Название переводится с португальского, как Луна, причем создатели убедительно просят не писать LUA, чтобы, не дай Бог, кто-нибудь не принял название за аббревиатуру. Является мультипарадигмальным скриптовым языком, использующим прототипную модель ООП.

Типизация здесь динамическая, а для реализации наследования используются метатаблицы, то есть это прекрасный инструмент для расширений возможностей вашего продукта. Причем из-за своей компактности он пригоден для использования практически на любой платформе. Посудите сами: tarball Lua 5.3.4 весит всего 296 килобайт (в “разжатом” виде — 1.1 мегабайт), интерпретатор (написанный на C) для Linux — от 182 до 246 килобайт, а стандартный набор библиотек — ещё 421 килобайт.

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

Начнем с традиционного:

Согласитесь, знакомо и не слишком информативно. Более интересный пример с точки зрения знакомства с Lua — вычисление факториала введенного числа:

function fact (n)
if n == 0 then
return 1
else
return n * fact(n-1)
end
end

print("enter a number:")
a = io.read("*number") — read a number
print(fact(a))

Все предельно понятно. Кстати, в Lua поддерживается параллельное присваивание:

И в заключении довольно простой пример с использованием библиотек:

#include
#include
#include
#include
#include

int main (void) <
char buff[256];
int error;
lua_State *L = lua_open(); /* opens Lua */
luaopen_base(L); /* opens the basic library */
luaopen_table(L); /* opens the table library */
luaopen_io(L); /* opens the I/O library */
luaopen_string(L); /* opens the string lib. */
luaopen_math(L); /* opens the math lib. */

while (fgets(buff, sizeof(buff), stdin) != NULL) <
error = luaL_loadbuffer(L, buff, strlen(buff), "line") ||
lua_pcall(L, 0, 0, 0);
if (error) <
fprintf(stderr, "%s", lua_tostring(L, -1));
lua_pop(L, 1); /* pop error message from the stack */
>
>

Преимущества и недостатки

Итак, чем же хорош Lua?

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

Во-вторых, он быстрый. Если взглянуть на сравнительную характеристику с другими языками, то можно заметить, что хоть Lua и не укладывает на лопатки C или Python, но в некоторых тестах показывает отличные результаты.

В-третьих, он очень удобен для изучения даже не самыми опытными программистами. Наверняка даже те, кто просто знает английский язык, поняли минимум 80% описанного выше кода и без проблем смогут воспроизвести.

Четвертый аргумент — у вас не возникнет никаких проблем с поиском информации. У Lua есть много полезной информации непосредственно на официальном сайте. Кроме того, очень недурно развито сообщество на StackOverFlow и IRC-чате, а в США разработчики и создатели так и вовсе встречаются каждый год.

Любителей современных тенденций привлечет тот факт, что на Lua можно писать функциональный код.Да что там, если вы хотя бы пару недель уделите программированию на Lua, то наверняка найдёте ещё не один десяток плюсов.

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

Хотите стать веб-разработчиком? Тогда вам на наш курс обучения web-разработке!

Среды разработки

LDT (Lua Development Tools) для Eclipse — расширение для одной из наиболее популярных IDE;

ZeroBrane Studio — специализированная среда, написанная на Lua;

Decoda — не самая популярная кроссплатформенная IDE, но в качестве альтернативы подойдет;

SciTE — хороший редактор, полноценно поддерживающий Lua;

WoWUIDesigner — угадайте, для какой игры эта среда помогает обрабатывать скрипты, в том числе на Lua?

Lua предлагает высокоуровневую абстракцию без потери связи с аппаратурой

В то время как интерпретируемые языки программирования, такие как Perl, Python, PHP и Ruby, пользуются все большей популярностью для Web-приложений (и уже давно предпочитаются для автоматизации задач по системному администрированию), компилируемые языки программирования, такие как C и C++, по-прежнему необходимы. Производительность компилируемых языков программирования остается несравнимой (она уступает только производительности ручного ассемблирования), поэтому некоторое программное обеспечение (включая операционные системы и драйверы устройств) может быть реализована эффективно только при использовании компилируемого кода. Действительно, всегда, когда программное и аппаратное обеспечение нужно плавно связать между собой, программисты инстинктивно приходят к компилятору C: C достаточно примитивен для доступа к "голому железу" (то есть, для использования особенностей какой-либо части аппаратного обеспечения) и, в то же время, достаточно выразителен для описания некоторых высокоуровневых программных конструкций, таких как структуры, циклы, именованные переменные и области видимости.

Однако языки сценариев тоже имеют четкие преимущества. Например, после успешного переноса интерпретатора языка на другую платформу подавляющее большинство написанных на этом языке сценариев работает на новой платформе без изменений, не имея зависимостей, таких как системные библиотеки функций (представьте множество DLL-файлов операционной системы Microsoft® Windows® или множество libcs на UNIX® и Linux®). Кроме того, языки сценариев обычно предлагают высокоуровневые программные конструкции и удобные операции, которые программистам нужны для повышения продуктивности и скорости разработки. Более того, программисты, использующие язык сценариев, могут работать быстрее, поскольку этапы компиляции и компоновки не нужны. В сравнении с С и его родственниками цикл "кодирование, компоновки, связывание, запуск" сокращается до ускоренного "написание, запуск".

Новшества в Lua

Как и любой язык сценариев, Lua имеет свои особенности:

  • Типы в Lua. В Lua значения имеют тип, но переменные типизируются динамически. Типы nil, boolean, number и string работают так, как вы могли бы ожидать.
  • Nil — это тип специального значения nil ; используется для представления отсутствия значения.
  • Boolean — это тип констант true и false (Nil тоже представляет значение false , а любое не nil значение представляет true ).
  • Все числа в Lua имеют тип doubles (но вы можете легко создать код для реализации других числовых типов).
  • string — это неизменяемый массив для символов (следовательно, для добавления к строке вы должны сделать ее копию).
  • Типы table, function и thread являются ссылками. Каждый такой тип может быть назначен переменной, передаваемой в качестве аргумента, или возвращаемой из функции. Ниже приведен пример сохранения функции:
  • Потоки в Lua. Поток — это сопрограмма, создаваемая вызовом встроенной функции coroutine.create(f) , где f — это функция Lua. Потоки не запускаются при создании; они запускаются позже при помощи функции coroutine.resume(t) , где t — это поток. Каждая сопрограмма может время от времени отдавать процессор другим сопрограммам при помощи функции coroutine.yield() .
  • Выражения присваивания. Lua разрешает множественные присваивания, и выражения сначала вычисляются, а затем присваиваются. Например, результат выражений
  • равен 4 7 5 nil nil . Если список переменных больше, чем список значений, лишним переменным присваивается значение nil ; поэтому b равно nil . Если значений больше, чем переменных, лишние значения просто игнорируются. В Lua названия переменных зависят от регистра символов, что объясняет, почему переменная I равна nil .

  • Порции (chunks). Порцией называется любая последовательность Lua-операторов. Порция может быть записана в файл или в строку в Lua-программе. Каждая порция выполняется как тело анонимной функции. Следовательно, порция может определять локальные переменные и возвращать значения.
  • Дополнительные интересные возможности. Lua имеет сборщик мусора "отметь и выкинь". В Lua 5.1 сборщик мусора работает в инкрементном режиме. Lua имеет полное лексическое замыкание (как Scheme, но не как Python). Кроме того, Lua имеет надежную семантику последовательных вызовов (tail call) (опять же, как Scheme, но не как Python).
  • Большее количество примеров Lua-кода приведено в руководстве "Программирование в Lua" и в wiki Lua-пользователей (ссылки приведены в разделе "Ресурсы").

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

    Беря все лучшее из обоих миров

    Что, если бы вы могли взять лучшее из обоих миров: производительность работы с "голым железом" и высокоуровневые, мощные абстракции? Более того, если бы вы могли оптимизировать алгоритмы и функции, зависящие от системы и требующие много процессорного времени, так же как и отдельную логику, не зависящую от системы и очень чувствительную к изменениям требований?

    Баланс требований для высокопроизводительного кода и высокоуровневого программирования является сутью Lua, встраиваемого языка программирования. Приложения, включающие Lua, представляют собой комбинацию компилируемого кода и Lua-сценариев. Компилируемый код может при необходимости заняться железом, и, в то же время, может вызывать Lua-сценарии для обработки сложных данных. И поскольку Lua-сценарии отделены от компилируемого кода, вы можете изменять сценарии независимо от него. С Lua цикл разработки более похож на "Кодирование, компоновка, запуск, создание сценариев, создание сценариев, создание сценариев …".

    Например, на странице "Uses" Web-сайта Lua (см. раздел "Ресурсы") перечислены некоторые компьютерные игры для массового рынка, включая World of Warcraft и Defender (версия классической аркады для бытовых консолей), которые интегрируют Lua для запуска всего, начиная с пользовательского интерфейса и заканчивая искусственным интеллектом противника. Другие приложения Lua включают в себя механизмы расширения для популярного инструментального средства обновления Linux-приложений apt-rpm и механизмы управления чемпионатом Robocup 2000 "Сумасшедший Иван". На этой странице есть много хвалебных отзывов о маленьком размере и отличной производительности Lua.

    Начало работы с Lua

    Lua версии 5.0.2 на момент написания данной статьи была текущей версией (недавно появилась версия 5.1). Вы можете загрузить исходный код Lua с lua.org, а можете найти различные предварительно откомпилированные двоичные файлы на wiki Lua-пользователей (ссылки приведены в разделе "Ресурсы"). Полный код ядра Lua 5.0.2, включая стандартные библиотеки и Lua-компилятор, по размерам не превышает 200KB.

    Если вы работаете на Debian Linux, то можете быстро и просто установить Lua 5.0 при помощи следующей команды

    с правами суперпользователя. Все приведенные здесь примеры запускались на Debian Linux "Sarge" с использованием Lua 5.0.2 и ядра Linux 2.4.27-2-686.

    После установки Lua на вашей системе попробуйте автономный Lua-интерпретатор. Все Lua-приложения должны быть встроены в базовое приложение. Интерпретатор — это просто специальный тип базового приложения, используемого для разработки и отладки. Создайте файл factorial.lua и введите в него следующие строки:

    Код в factorial.lua (точнее, любая последовательность Lua-операторов) называется порцией (chunk), как было описано выше в разделе "Новшества в Lua". Для запуска созданной вами порции выполните команду lua factorial.lua :

    Или, как в других языках сценариев, вы можете добавить строку со знаками ( #! ) ("shebang") в начало сценария, делая сценарий исполняемым, а затем запустить файл как автономную команду:

    Язык Lua

    Lua обладает многими удобствами, имеющимися в современных языках программирования сценариев: область видимости, управляющие структуры, итераторы и стандартные библиотеки для обработки строк, выдачи и сбора данных и выполнения математических операций. Полное описание языка Lua приведено в "Справочном руководстве по Lua 5.0" (см. раздел "Ресурсы").

    В Lua тип имеют только значения, а переменные типизируются динамически. В Lua есть восемь фундаментальных типов (или значений): nil, boolean, number, string, function, thread, table и userdata. Первые шесть типов говорят сами за себя (исключения приведены в разделе "Новшества в Lua"); два последних требуют пояснения.

    Таблицы в Lua

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

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

    Для исследования таблиц запустите Lua-интерпретатор и введите строки, показанные жирным шрифтом в листинге 1.

    Листинг 1. Экспериментируя с таблицами Lua

    Как вы могли ожидать, Lua также предоставляет несколько функций-итераторов для обработки таблиц. Функции предоставляет глобальная переменная table (да, Lua-пакеты — это тоже просто таблицы). Некоторые функции, например table.foreachi() , ожидают непрерывный диапазон целых ключей, начиная с 1 (цифра один):

    Другие, например table.foreach() , выполняют итерацию по всей таблице:

    Хотя некоторые итераторы оптимизированы для целых индексов, все они просто обрабатывают пары (ключ, значение).

    Ради интереса создайте таблицу t с элементами <2, 4, 6, language="Lua", version="5", 8, 10, 12, web="www.lua.org">и выполните команды table.foreach(t, print) и table.foreachi(t, print) .

    Userdata

    Поскольку Lua предназначен для встраивания в базовое приложение, написанное на таких языках, как, например, C или C++, для взаимодействия с базовым приложением данные должны совместно использоваться средой C и Lua. Как указано в "Справочном руководстве по Lua 5.0", тип userdata позволяет "произвольным C-данным храниться в Lua-переменных". Вы можете рассматривать тип userdata как массив байтов — байтов, которые могут представлять указатель, структуру или файл в базовом приложении.

    Содержимое userdata происходит от C, поэтому оно не может быть модифицировано в Lua. Естественно, поскольку userdata происходит от C, в Lua не существует предопределенных операций для userdata. Однако вы можете создать операции, которые работают с userdata , используя еще один механизм Lua, называемый мета-таблицами (metatables).

    Мета-таблицы

    Из-за такой гибкости типов table и userdata Lua разрешает перегружать операции для объектов каждого из этих типов (вы не можете перегружать шесть остальных типов). Мета-таблица — это (обычная) Lua-таблица, которая отображает стандартные операции в предоставляемые вами пользовательские функции. Ключи мета-таблицы называются событиями (event); значения (другими словами, функции) называются мета-методами (metamethod).

    Функции setmetatable() и getmetatable() изменяют и запрашивают мета-таблицу объекта соответственно. Каждый объект table и userdata может иметь свою собственную мета-таблицу.

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

    Эта порция отображает следующий текст:

    Функция function String() принимает строку ( string ), заключает ее в таблицу ( ) и назначает мета-таблицу mt этой таблице. Функция mt.__add() является мета-методом, добавляющим строку b к строке, находящейся в a.value b раз. Строка print((s + ‘ There ‘ + ‘ World!’).value ) активизирует мета-метод дважды.

    __index — это еще одно событие. Мета-метод для __index вызывается всегда, когда ключ в таблице не существует. Вот пример, который запоминает ("memoizes") значение функции:

    Поместите этот код в Lua-интерпретатор и введите print(color[1], color[2], color[1]) . Вы должны увидеть что-то подобное blue black blue .

    Этот код, получающий ключ и узел, ищет цвет узла. Если он не существует, код присваивает узлу новый, выбранный случайно цвет. В противном случае возвращается цвет, назначенный узлу. В первом случае мета-метод __index выполняется один раз для назначения цвета. В последнем случае выполняется простой и быстрый поиск в хеш-таблице.

    Язык Lua предлагает много мощных функциональных возможностей, и все они хорошо документированы. Но всегда, когда вы столкнетесь с проблемами или захотите пообщаться с мастером, обратитесь за поддержкой к энтузиастам — IRC-канал Lua Users Chat Room (см. раздел "Ресурсы").

    Встроить и расширить

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

    Сердцем симбиотического взаимодействия между Lua и его базовым языком является виртуальный стек. Виртуальный стек (как и реальный) является структурой данных "последний вошел — первый вышел" (last in-first out — LIFO), которая временно сохраняет аргументы функции и ее результаты. Для вызова из Lua базового языка (и наоборот) вызывающая сторона помещает значения в стек и вызывает целевую функцию; принимающая сторона достает аргументы из стека (конечно же, проверяя тип и значение каждого аргумента), обрабатывает данные и помещает в стек результаты. Когда управление возвращается вызывающей стороне, она извлекает значения из стека.

    Фактически, все С-интерфейсы прикладного программирования (API) для Lua-операций работают через стек. Стек может хранить любое Lua-значение; однако тип значения должен быть известен как вызывающей стороне, так и вызываемой, а конкретные функции помещают в стек и извлекают из него каждый тип (например, lua_pushnil() и lua_pushnumber() ).

    В листинге 2 показана простая C-программа (взятая из главы 24 книги "Программирование в Lua", ссылка на которую приведена в разделе "Ресурсы"), реализующая минимальный, но функциональный Lua-интерпретатор.

    Листинг 2. Простой Lua-интерпретатор

    Строки с 2 по 4 включают стандартные Lua-функции, несколько удобных функций, используемых во всех Lua-библиотеках, и функции для открытия библиотек, соответственно. Строка 9 создает Lua-структуру. Все структуры сначала пусты; вы добавляете библиотеки или функции к структуре при помощи luaopen_. () , как показано в строках с 10 по 14.

    В строке 17 luaL_loadbuffer() принимает входную информацию с stdin в виде порции и компилирует ее, помещая порцию в виртуальный стек. Строка 18 извлекает порцию из стека и выполняет ее. Если во время исполнения возникает ошибка, Lua-строка помещается в стек. Строка 20 обращается к вершине стека (вершина стека имеет индекс -1 ) как к Lua-строке, распечатывает сообщение и удаляет значение из стека.

    Используя C API, ваше приложение может также "достать" информацию из Lua-структуры. Следующий фрагмент кода извлекает две глобальные переменные из Lua-структуры:

    Опять же, обратите внимание на то, что передачу разрешает стек. Вызов любой Lua-функции из C аналогичен следующему коду: извлечь функцию при помощи lua_getglobal() , поместить аргументы, выполнить lua_pcall() и обработать результаты. Если Lua-функция возвращает n значений, первое значение находится по индексу -n в стеке, а последнее — по индексу -1.

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

    Lua великолепен

    Lua — это чрезвычайно легкий в использовании язык, но его простой синтаксис маскирует его мощь: язык поддерживает объекты (аналогичные объектам Perl), мета-таблицы делают его тип table абсолютно гибким, а C API разрешает отличную интеграцию и расширение сценариев и базового языка. Lua может использоваться совместно с языками C, C++, C#, Java™ и Python.

    Перед созданием еще одного формата конфигурационного файла или ресурса (и еще одного синтаксического анализатора для него) попробуйте Lua. Язык Lua (так же как и его сообщество) надежен, изобретателен и готов прийти на помощь.

    Ресурсы для скачивания

    Похожие темы

    • Оригинал статьи "Embeddable scripting with Lua".
    • "Справочное руководство по Lua 5.0" полностью описывает язык Lua.
    • Книга "Программирование на Lua" Роберто Иерусалимши (Roberto Ierusalimschy) (Roberto Ierusalimschy, 2003) является бесценным ресурсом для изучения эффективного кодирования в Lua.
    • Обращайтесь к библиотеке руководств по Lua в wiki пользователей Lua для использования конкретных возможностей Lua.
    • Список проектов, использующих Lua.
    • Получите исходный код Lua 5.0.2 или Lua 5.1 для построения Lua с нуля.
    • В wiki пользователей Lua находятся предварительно откомпилированные, готовые для установки двоичные коды Lua.
    • На LuaForge размещено огромное количество библиотек кода, включая связывание со многими языками и специализированные вычислительные библиотеки.
    • Используйте в вашем следующем проекте с открытым исходным кодом пробное программное обеспечение IBM, доступное для загрузки непосредственно с developerWorks.

    Комментарии

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

    Луа (Lua) — мощный, быстрый, лёгкий, расширяемый и встраиваемый скриптовый язык программирования. Луа удобно использовать для написания бизнес-логики приложений.

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

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

    Пример

    В качестве наивного примера возьмём код создания диалогового окна с текстовым сообщением и кнопкой в императивном стиле:

    function build_message_box ( gui_builder )

    local my_dialog = gui_builder:dialog ( )

    my_dialog:set_title ( "Message Box" )

    local my_label = gui_builder:label ( )

    my_label:set_text ( "Hello, world!" )

    local my_button = gui_builder:button ( )

    В декларативном стиле этот код мог бы выглядеть так:

    build_message_box = gui:dialog "Message Box"

    Гораздо нагляднее. Но как сделать, чтобы это работало?

    Основы

    Чтобы разобраться в чём дело, нужно знать о некоторых особенностях языка Луа. Я поверхностно расскажу о самых важных для понимания данной статьи. Более подробную информацию можно получить по ссылкам ниже.

    Динамическая типизация

    Важно помнить, что Луа — язык с динамической типизацией. Это значит, что тип в языке связан не с переменной, а с её значением. Одна и та же переменная может принимать значения разных типов:

    a = "the meaning of life" —> была строка,

    Таблицы

    Таблицы (table) — основное средство композиции данных в Луа. Таблица — это и record и array и dictionary и set и object.

    Для программирования на Луа очень важно хорошо знать этот тип данных. Я кратко остановлюсь лишь на самых важных для понимания деталях.

    Создаются таблицы при помощи «конструктора таблиц» (table constructor) — пары фигурных скобок.

    Создадим пустую таблицу t:

    Запишем в таблицу t строку «one» по ключу 1 и число 1 по ключу «one»:

    Содержимое таблицы можно указать при её создании:

    Таблица в Луа может содержать ключи и значения всех типов (кроме nil). Но чаще всего в качестве ключей используются целые положительные числа (array) или строки (record / dictionary). Для работы с этими типами ключей язык предоставляет особые средства. Я остановлюсь только на синтаксисе.

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

    Следующие две формы записи эквивалентны:

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

    При создании таблицы следующие две формы записи эквивалентны:

    Аналогично для индексации при записи…

    Функции

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

    Функции в Луа можно создавать динамически в любом месте кода. При этом внутри функции доступны не только её аргументы и глобальные переменные, но и локальные переменные из внешних областей видимости. Функции в Луа, на самом деле, это замыкания (closures).

    function make_multiplier ( coeff )

    return function ( value )

    return value * coeff

    local x5 = make_multiplier ( 5 )

    print ( x5 ( 10 ) ) —> 50

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

    Следующие два способа создания функции эквивалентны. Создаётся новая функция и присваивается глобальной переменной mul.

    function mul ( lhs, rhs ) return lhs * rhs end

    mul = function ( lhs, rhs ) return lhs * rhs end

    Вызов функции без круглых скобок

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

    print ( "Use the force," , name )

    my_name_is "Luke" —> Use the force, Luke

    print ( "Shopping list:" )

    for name, qty in pairs ( items ) do

    print ( "*" , qty, "x" , name )

    Цепочки вызовов

    Как я уже упоминал, функция в Луа может вернуть другую функцию (или даже саму себя). Возвращённую функцию можно вызвать сразу же:

    chain_print ( 1 ) ( "alpha" ) ( 2 ) ( "beta" ) ( 3 ) ( "gamma" )

    В примере выше можно опустить скобки вокруг строковых литералов:

    chain_print ( 1 ) "alpha" ( 2 ) "beta" ( 3 ) "gamma"

    Для наглядности приведу эквивалентный код без «выкрутасов»:

    local tmp1 = chain_print ( 1 )

    local tmp2 = tmp1 ( "alpha" )

    local tmp3 = tmp2 ( 2 )

    local tmp4 = tmp3 ( "beta" )

    local tmp5 = tmp4 ( 3 )

    Методы

    Объекты в Луа — чаще всего реализуются при помощи таблиц.

    За методами, обычно, скрываются значения-функции, получаемые индексированием таблицы по строковому ключу-идентификатору.

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

    Следующие три формы записи эквивалентны. Создаётся глобальная переменная myobj, в которую записывается таблица-объект с единственным методом foo.

    function myobj:foo ( b )

    print ( self.a_ + b )

    function myobj.foo ( self, b )

    print ( self.a_ + b )

    myobj.foo ( myobj, 37 ) —> 42

    Совсем без сахара:

    myobj [ "foo" ] = function ( self, b )

    print ( self [ "a_" ] + b )

    myobj [ "foo" ] ( myobj, 37 ) —> 42

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

    get_myobj ( ) .foo ( get_myobj ( ) , 37 )

    Чтобы код был эквивалентен варианту с двоеточием, нужна временная переменная:

    local tmp = get_myobj ( )

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

    Реализация

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

    build_message_box = gui:dialog "Message Box"

    Что же там написано?

    Приведу эквивалентную реализацию без декларативных «выкрутасов»:

    local tmp_1 = gui : label ( "Hello, world!" )

    local tmp_2 = gui : button ( "OK" )

    local button = tmp_2 ( < >)

    local tmp_3 = gui : dialog ( "Message Box" )

    Интерфейс объекта gui

    Как мы видим, всю работу выполняет объект gui — «конструктор» нашей функции build_message_box(). Теперь уже видны очертания его интерфейса.

    Опишем их в псевдокоде:

    Декларативный метод

    В интерфейсе объекта gui чётко виден шаблон — метод, принимающий часть аргументов и возвращающий функцию, принимающую остальные аргументы и возвращающую окончательный результат.

    Для простоты, будем считать, что мы надстраиваем декларативную модель поверх существующего API gui_builder, упомянутого в императивном примере в начале статьи. Напомню код примера:

    function build_message_box ( gui_builder )

    local my_dialog = gui_builder:dialog ( )

    my_dialog:set_title ( "Message Box" )

    local my_label = gui_builder:label ( )

    my_label:set_text ( "Hello, world!" )

    local my_button = gui_builder:button ( )

    Попробуем представить себе, как мог бы выглядеть метод gui:dialog():

    return function ( element_list )

    return function ( gui_builder )

    local my_dialog = gui_builder:dialog ( )

    for i = 1 , #element_list do

    element_list [ i ] ( gui_builder )

    Ситуация с [gui_element] прояснилась. Это — функция-конструктор, создающая соответствующий элемент диалога.

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

    Напрашивается как минимум такое обобщение:

    function declarative_method ( method )

    return function ( self, name )

    return function ( data )

    return method ( self, name, data )

    Теперь gui:dialog() можно записать нагляднее:

    gui.dialog = declarative_method ( function ( self, title, element_list )

    return function ( gui_builder )

    local my_dialog = gui_builder:dialog ( )

    for i = 1 , #element_list do

    element_list [ i ] ( gui_builder )

    Реализация методов gui:label() и gui:button() стала очевидна:

    gui.label = declarative_method ( function ( self, text, parameters )

    return function ( gui_builder )

    local my_label = gui_builder:label ( )

    if parameters.font_size then

    gui.button = declarative_method ( function ( self, title, parameters )

    return function ( gui_builder )

    local my_button = gui_builder:button ( )

    — Так сложилось, что у нашей кнопки нет параметров.

    Что же у нас получилось?

    Проблема улучшения читаемости нашего наивного императивного примера успешно решена.

    В результате нашей работы мы, фактически, реализовали с помощью Луа собственный предметно-ориентированный декларативный язык описания «игрушечного» пользовательского интерфейса (DSL).

    Благодаря особенностям Луа реализация получилась дешёвой и достаточно гибкой и мощной.

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

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

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

    Полностью работающий пример можно посмотреть здесь.


    [an error occurred while processing the directive]
    Карта сайта