Крихітка Lua
Серед звичних та відомих всім у світі ІТ великих мов програмування можуть загубитись маленькі перлини. І мова йде не про perl, що з кожним роком стає за моїми спостереженнями все менш популярним, дарма що це одна з найпотужніших мов, яка обов'язково заслуговує на увагу до себе. Мова йде про ще більш "загублені" мови, які навіть ніколи не досягали популярності perl'а. Відверто кажучи я й сам особливо не звертав на такі мови програмування уваги, а от коли довелось познайомитись з мініатюрною скриптовою мовою Lua, я не пожалкував ні про один день з тих двох тижнів, що були проведені з цією крихіткою.
Lua ([лу́а], порт. місяць) - "ще одна" інтерпретована мова програмування, що є вільно розповсюджуваною, має відкритий програмний код мовою С та широко відома у вузьких колах розробників... та на відміну від більшості інших подібних мов, про неї чули також і допитливі фанати комп'ютерних ігор. Чому? Та тому, що це скриптова мова, що використовується в популярних World of Warcraft, The Witcher, Warhammer Online, S.T.A.L.K.E.R. та ще десятки хітів.
Думаю вже цього має вистачити для того, щоб зацікавитись мовою. Не дарма ж розробники стількох проектів її використовують
Історія та ідеї
Історія мови розпочалась в 1993 р. Команда розробників Tecgraf з католицького університету Ріо-де-Жанейро невелика — Роберто Єрусалимський (Roberto Ierusalimschy), Вольдемар Целес (Waldemar Celes) і Луіс Енріке (Luiz Henrique), почали роботу над описовою мовою, що мала використовуватись для зручності формування великих масивів даних для розв'язання завдань обчислювального експерименту і машинного моделювання. Але в процесі розвитку мова набула значних змін та архітектурних модифікацій, ставши повноцінною скриптовою мовою з солідним набором можливостей.
Lua - мова з динамічною типізацією, і змінна може приймати значення будь-якого з восьми типів:
- nil (невизначений)
- boolean (логічний)
- number (числовий)
- string (рядковий)
- function (функцыя)
- userdata (даны користувача)
- thread (потік)
- table (таблиця)
Тип nil використовується для позначення відсутності значення (що є звичним для людей знайомих з Pascal).Boolean може приймати два значення true (істинне) та false (хибне), причому nil відповідає false, а будь-яке інше значення - true. Number - дійсні змінні з подвійною точністю. String - незмінювані масиви 8-бітних символів, тобто для кожного нового значення змінної буде виділятись новий рядок.
Все вищеописане є простим та звичним. А ось далі починається найбільш цікаве. Таблиці (table) є базовим елементом для створення масивів, списків, структур та множин. Кожна таблиці - набір пар (ключ, значення), де ключ може приймати значення значення будь-якого типу даних, окрім nil.
Також можна помітити, що в мові функції можуть бути значеннями змінних і як можна легко здогадатись ця мова підтримує замикання. Дозвольте мені повернутись до цього пізніше і перед практичною частиною перерахувати ще декілька речей, які роблять Lua популярним.
Так само як і JavaScript ця маленька мова підтримує для об'єктів механізм прототипів, роблячи це легко та елегантно. Метатаблиці дозволяють організувати повноцінну підтримку ООП включаючи множинне успадкування, перевантаження операції і т.д.
Також не останню роль грає легкість вбудовування в інші проекти (як саме вбудовування інтерпретатора в програму так і додавання нових функцій написаних мовами C чи C++ для можливого виклику цих функцій з Lua). Ось так буде виглядати мінімально-функціональний інтерпретатор Lua з використанням мови C:
#include <stdio.h>
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
int main (void) {
char buff[256];
int error;
lua_State *L = lua_open(); /* відкиває Lua */
luaopen_base(L); /* відкриває основну бібліотеку */
luaopen_table(L); /* відкриває бібліотеку table */
luaopen_io(L); /* відкриває бібліотеку I/O */
luaopen_string(L); /* відкриває бібліотеку string */
luaopen_math(L); /* відкриває бібліотеку math */
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); /* повідомлення про помилку зі стеку */
}
}
lua_close(L);
return 0;
}
І остання особливість мови: досить висока швидкодія як для скриптової мови та наявність JIT (just in time) компілятора LuaJIT (плюс компілятор llvm-lua для віртуальної машини LLVM). Що при наявності можливостей для легкого імпорту функцій з C дозволяє розробляти проекти з високими вимогами до оперативності.
Трошки практики
Не можна розповідати про мову програмування не показавши її в дії. Почну з традиційного "Hello, world!":
print("Hello, world!")
Оскільки Lua -інтерпретатор, то думаю, що особливих проблем з викликом коду не буде. Потрібно лише встановити інтерпретатор, а потім можна скористуватись командним рядком. Для Debain-based дистрибутивів процедура встановлення матиме вигляд:
# apt-get install lua50
Для Windows можна скористатись пакетом Lua for Windows, що включає в себе окрім самого інтерпретатора набір бібліотек, документацію для цих бібліотек, близько 250 готових скриптів-прикладів та навіть текстовий редактор, для написання програмного коду.
Після "Hello, world" слід переходити до більш серйозних та інформативний прикладів.
Змінні в цій мові можуть бути глобальними та локальними:
a = 1 -- глобальна змінна
local b = 2 -- локальна змінна
Останні доступні лише в межах блоків, що в межах термінології Lua називаються порціями коду (chunk):
do
-- тіло блоку
end
Області видимості вкладаються, тобто дочірній блок матиме доступ до всіх локальних змінних батьківського блоку. Блоки використовуються як і у інших мовах програмування разом з операторами. Найпростіший оператор if:
a = 1
b = 2
if a ~= b then
print("not equals")
end
Як Ви могли помітити, ніде в наведеному коді немає традиційних для багатьох мов ";". Можу Вас заспокоїти, використання "крапки з комою" можливе, як для розділу окремих рядків коду так і для розділу одного рядка на окремі операції. Звичайно ж, якщо Вам це зручно. Мені особисто не дуже, тому в прикладах я пишу без їх використання.
Повертаючись до мови Lua розповім про ще декілька приємних нюансів роботи зі змінними. Як і Python тут є можливість виконувати паралельне присвоювання:
x, y = y, x
В результаті виконання наведеного вище коду, змінні x та y обміняються значеннями.
Оскільки Lua має динамічну типізацію, то зведення типів є доволі легкою справою. Ось так наприклад буде виглядати конкатенація:
local a = "one"
local b = 2
print(a..", "..b)
Однак поряд з динамічним зведенням типів в ряді випадків необхідно скористатись функціями зведення типів, такими як tonumber, tostring і т.д. Зазвичай це корисно для роботи з даними користувача чи для роботи зі складними структурами.
Ще однією приємної дрібницею в роботі зі змінними є можливість виконання присвоєння у випадку, коли змінна не була проініціалізована раніше:
var = var or 0
Уявити мову без циклів більшості сучасних програмістів є завданням непростим і звичайно ж Lua має цілий набір операторів циклів:
while вираз_умова do
тіло_блоку
end
repeat
тело_блоку
until вираз_умова
for змінна=початкове_значення, кінцеве_значення, крок do
тіло_блоку
end
for змінна_циклу, додаткова_змінна in інтекактивний_вираз do
тело_блока
end
Наприклад:
a = 1
b = 5
c = 2
while a < b and c < b do
c = a+1
a = c+1
end
Нічого змістовного тут не робиться, однак має працювати :)
Хочу також навести приклад використання останнього типу циклу. Однак для цього також треба розібратись з синтаксисом роботи з таблицями. Створити просту таблицю можна так:
t1 = {}
t1[1] = "one"
t1[2] = 2
t1["3"] = true
, або так:
t2 = {[1] = "one", [2] = 2, ["3"] = true}
Обидва приклади створюють ідентичні зі індексами та відповідними значеннями таблиці. Доступ до елементів таблиці виконується за ключем:
print(t1[1]..", "..t2["3"])
Тепер можна роздрукувати значення елементів таблиці за допомогою циклу:
for key, value in pairs(t1) do
print(key.." -- "..value)
end
Також можна піти іншим шляхом, для виконання ітеративних операцій з таблицями:
table.foreach(t2, print)
table.foreachi(t2, print)
Пакети в Lua також є таблицями і глобальна змінна table містить набір ітераторів для работи з таблицями. Функція foreach виконує ітерацію за всією таблицєю, а функція foreachi виконує ітераціє за ключами таблиці, що є цілими числами.
Дуже потужним інструментом для цієї скриптової мови є функції. Так для прикладу напишемо функцію для розрахунку факторіалу:
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")
print(fact(a))
Виконавши цей код отримаємо:
$ lua factorial.lua
enter a number:
10
3628800
Як і обіцяв скажу ще декілька слів про механізм замикання. Краще це показати на прикладі:
function make_func(x)
return function(y)
return x + y
end
end
plus_func = make_func(2)
print(plus_func(5))
В прикладі функція make_func повертає в результаті свого виконання анонімну функцію. Під час створення цієї функції Lua знаходить змінну, що знаходиться поза областю цієї функції, тому створює замикання використовуючи значення параметру функції-генератора (в нашому прикладі make_func). В результаті виконання цього прикладу отримаємо надруковане в терміналі значення 7.
Коли я розповідав про ідеї і можливості Lua, я згадав також його потужні можливості для роботи з прототипами. Настав час показати невеликий приклад, для демонатрції цих можливостей:
human = {
type = "Humam"
}
function human:say_hello()
print("Hello from "..self.type.." "..self.name)
end
human.name = "Jack"
human:say_hello()
human.say_hello(human)
human["say_hello"](human)
Цей код створюэ таблицю human, що містить елемент type, якому одразу встановлюється значення human. Потім до цієї таблиці додається як елемент функція say_hello, що завдяки оператору ":" отримує доступ до параметру self, таким чином реалізуючи інкапсуляцію. Після цього встановлюється значення поля name та тричі різним способов виконується виклик "методу об'єкту human". В результаті виконання отримаємо тричі надруковане "Hello from Human Jack".
І це не все...
Звичайно, що мій огляд не претендує на повноцінний та всеосяжний мануал, який дозволяє написати будь-яку програму мовою Lua. Підозрюю, що й на звання повного огляду всіх особливостей та функцій моя писанина теж претендувати не може. Це лише мої замітки, отримані в результаті двох тижнів, проведених в роботі з цією мовою в рамках прикладної задачі. Хоча сам той факт, що мені після двох тижнів роботи захотілось поділитись зі світом враженнями та те що з цією мовою я добре почувався вже на першому тижні знайомства розуміючи написаний код і без особливих проблем та дуже частих зазирань у всесвітню мережу вже писав свій власний, впевнений має змусити звернути когось хоча б невелику увагу на цю мову. Хоча б тому, що багато часу це не займе.
Зі святом Всіх! Дякую за увагу!
Коментарі:
Оффтоп. В CMS є деякі глюки.
Наприклад підсвітка синтаксису кульгає. Замість < і > треба ставити < і >
#include
#include
#include
#include
А про lua ще подумаю.
У Вас в слове «луа» ударение неправильно стоит.



