Крихітка Lua

Опубліковано: 2009-06-28   01:35:35

WorkСеред звичних та відомих всім у світі ІТ великих мов програмування можуть загубитись маленькі перлини. І мова йде не про perl, що з кожним роком стає за моїми спостереженнями все менш популярним, дарма що це одна з найпотужніших мов, яка обов'язково заслуговує на увагу до себе. Мова йде про ще більш "загублені" мови, які навіть ніколи не досягали популярності perl'а. Відверто кажучи я й сам особливо не звертав на такі мови програмування уваги, а от коли довелось познайомитись з мініатюрною скриптовою мовою Lua, я не пожалкував ні про один день з тих двох тижнів, що були проведені з цією крихіткою.

Lua ([лу́а], порт. місяць) - "ще одна" інтерпретована мова програмування, що є вільно розповсюджуваною, має відкритий програмний код мовою С та широко відома у вузьких колах розробників... та на відміну від більшості інших подібних мов, про неї чули також і допитливі фанати комп'ютерних ігор. Чому? Та тому, що це скриптова мова, що використовується в популярних World of Warcraft, The Witcher, Warhammer Online, S.T.A.L.K.E.R. та ще десятки хітів.

World

Думаю вже цього має вистачити для того, щоб зацікавитись мовою. Не дарма ж розробники стількох проектів її використовують

Історія та ідеї

Історія мови розпочалась в 1993 р. Команда розробників Tecgraf з католицького університету Ріо-де-Жанейро невелика — Роберто Єрусалимський (Roberto Ierusalimschy), Вольдемар Целес (Waldemar Celes) і Луіс Енріке (Luiz Henrique), почали роботу над описовою мовою, що мала використовуватись для зручності формування великих масивів даних для розв'язання завдань обчислювального експерименту і машинного моделювання. Але в процесі розвитку мова набула значних змін та архітектурних модифікацій, ставши повноцінною скриптовою мовою з солідним набором можливостей.

Lua - мова з динамічною типізацією, і змінна може приймати значення будь-якого з восьми типів:

  1. nil (невизначений)
  2. boolean (логічний)
  3. number (числовий)
  4. string (рядковий)
  5. function (функцыя)
  6. userdata (даны користувача)
  7. thread (потік)
  8. 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. Підозрюю, що й на звання повного огляду всіх особливостей та функцій моя писанина теж претендувати не може. Це лише мої замітки, отримані в результаті двох тижнів, проведених в роботі з цією мовою в рамках прикладної задачі. Хоча сам той факт, що мені після двох тижнів роботи захотілось поділитись зі світом враженнями та те що з цією мовою я добре почувався вже на першому тижні знайомства розуміючи написаний код і без особливих проблем та дуже частих зазирань у всесвітню мережу вже писав свій власний, впевнений має змусити звернути когось хоча б невелику увагу на цю мову. Хоча б тому, що багато часу це не займе.

Зі святом Всіх! Дякую за увагу!

Теги: Lua
Коментарі: 3
 

Коментарі:

bunyk2009-07-29 22:24:29 :

Оффтоп. В CMS є деякі глюки.

Наприклад підсвітка синтаксису кульгає. Замість < і > треба ставити < і >

#include

#include

#include

#include

А про lua ще подумаю.

 
GrAndSE2009-09-05 08:51:22 :

Нічого не надумалось? ;)

 
Anonymus2009-09-25 22:59:50 :

У Вас в слове «луа» ударение неправильно стоит.

 

Додати коментар

user

email

url

text

Повідомляти про новікоментарі