Луа (програмски језик)

Луа је једноставан, рефлективан, императивни и функционални програмски језик, дизајниран као скрипт језик са проширивом семантиком као примарним циљем[1]. Само име језика потиче од португалске речи луа што значи месец. Језик је креиран 1993. године, а лиценциран је под МИТ лиценцом (до верзије 5.0 коришћена је БСД лиценца).

Lua
Logo
Modelobjektno-orijentisani, imperativni, funkcionalni, prototipski
Pojavio se1993
Aktuelna verzija5.3.4
Datum aktuelne verzije30. Januar 2017.
ImplementacijeFalcon, GameMonkey, JavaScript, Ruby
UticajiC++, Scheme, SNOBOL, Modula, CLU
Operativni sistemivišeplatformski
LicencaMIT
Veb-sajthttp://www.lua.org

Луа поседује релативно једноставан C АПИ у поређењу са осталим скрипт језицима. Луа и ЈаваСцрипт имају врло сличну семантику упркост великим разликама у синтакси. По дизајну, Луа је врло слична и програмском језику Ицон. Има широку примену у индустрији видео-игара[2], али и у неким комерцијалним као и некомерцијалним апликацијама.

Луа програми се не интерпретирају директно из текстуалне Луа датотеке, већ се компајлирају на бајткод који се потом извршава на Луа виртуелној машини. Коришћењем минималног скупа типова података, Луа покушава да балансира између величине и моћи.

Типови података[3] уреди

Као и већина скрипт језика, луа је динамички типизиран програмски језик. Постоји осам основних типова података: nil, boolean, number, string, userdata, function, thread и table. Функција type враћа тип променљиве чија вредност јој је прослеђена.

print(type("Pozdrav svete!"))

Као резултат овог позива, на екрану ће бити исписано string.

С обзиром на то да се променљиве не декларишу, једна иста променљива може узимати вредности различитог типа.

a = print
print(type(a))

Овако нешто је дозвољено пошто функција спада у основни тип података. На екрану ће приликом овог позива бити исписано function. Nil је тип са једном могућом вредношћу nil, која служи да учини променљиву различитом од осталих. Тј. говори нам да та променљива нема употребљиву вредност.

Буловски тип има две вредности: true и false и оне имају уобичајено значење. Number служи за представљање реалних бројева. Луа нема тип за посебно складиштење целих бројева. У оквиру типа number могу се представити 32-битни цели бројеви без проблема са заокруживањем.

Стрингови су имутабилни, што значи да у једној променљивој типа string није могуће мењати неки карактер појединачно, већ једино што може је да се креира нови стринг са одговарајућим изменама.

Табеле уреди

Једна од специфичности програмског језика Луа су табеле. Овај тип података имплементира придружене низове. Придружени низ је низ који не мора да буде индексиран само бројевима, већ се могу користити и стрингови, или друге вредности у језику не укључујући нил. Оно што је посебно добро код табеле јесте што немају фиксирану, унапред одређену величину, већ могу да се мењају динамички. Табеле у Луа нису ни вредности, ни променљиве, већ објекти.

a = {} -- kreira se tabela i njena referenca se čuva u 'a'
k = "x"
a[k] = 10 -- novi ulaz, sa ključem "x" i vrednošću 10 pridruženom tom ključu
a[20] = "great" -- novi ulaz, sa ključem 20 i vrednošću "great"
print(a["x"]) --> 10
k = 20
print(a[k]) --> "great"
a["x"] = a["x"] + 1 -- povećava vrednost čiji je ključ "x" za 1
print(a["x"]) --> 11

Не постоји фиксиран однос између табеле и променљиве која на њу реферише.

a = {}
a["x"] = 10
b = a -- 'b' referiše na istu tabelu kao i 'a'
print(b["x"]) --> 10
b["x"] = 20
print(a["x"]) --> 20
a = nil -- sada samo 'b' i dalje referiše na tabelu
b = nil -- nema više promenljivih koje referišu na datu tabelu

Када више нема променљивих које реферишу на неку табелу, Луа сакупљач отпада ће да избрише табелу и да искористи њену меморију за нешто друго.

Лакши запис за а["name"] где је а табела а name кључ је a.name.

Табеле су значајне, између осталог и због тога што се све уобичајене структуре које други програмски језици нуде - низови, листе, слогови, редови, скупови - могу представити помоћу табела у Луа. Такође, све наведене структуре Луа имплементира ефикасно. У традиционалним програмским језицима као што су C и Пасцал, већину структура података имплементирамо преко низова и листи. Иако низове и листе можемо имплементирати преко табела у Луа, што се понекад и ради, табеле су ипак моћније од низове и листи, толико да проблем сведу на тривијалан. На пример, претрага у Луа се лакше ради помоћу табела, јер код њих имамо директан приступ елементима.

Низови уреди

Имплементирање низова у Луа вршимо тако што индексирамо табелу целим бројевима. Сходно томе, низови немају фиксну дужину, већ се повећавају колико год да нам је потребно. Обично када иницијализујемо низ, ми му имплицитно одредимо и величину. Нпр. након следећег кода, било који покушај да приступимо неком пољу ван опсега 1-1000 резултираће са нил, уместо са нулом:

a = {} -- novi niz
for i=1, 1000 do
  a[i] = 0
end

Оператор # нам враћа дужину низа.

print(#a) --> 1000

Низ можемо почети од 0,1 или било ког другог броја:

-- kreira niz sa indeksima od -5 do 5
a = {}
for i=-5, 5 do
  a[i] = 0
end

Ипак, обичај је у Луа да се низ индексира од јединице. Библиотеке за Луа као и оператор #, сагласни су са овом конвенцијом. Ако корисник креира низ чији индекси не крећу од јединице, остаће ускраћен за коришћење ових олакшица. Можемо да користимо конструктор да креирамо и иницијализујемо низ у једном изразу:

kvadrati = {1, 4, 9, 16, 25, 36, 49, 64, 81}

Ови конструктори могу бити велики колико год нам је потребно (чак и до неколико милиона елемената).

Матрице и вишедимензионални низови уреди

У Луа матрице се могу представити на два начина. Једна је као низ низова, односно као табела, где је сваки елемент табела. Нпр. следећим кодом формира се матрица димензија N пута M, испуњена нулама:

mt = {} -- kreira se matrica
for i=1,N do
  mt[i] = {} -- kreira se novi red
  for j=1,M do
    mt[i][j] = 0
  end
end

Зато што су табеле објекти у Луа, сваки ред мора експлицитно да се креира, да би се креирала матрица. На први поглед може се учинити да овакав код није много различит од декларисања матрица у C-у и Пасцал-у, но ипак ако бисмо желели троугаону матрицу, мењањем петље фор ј=1,M до са фор ј=1,и до искористили бисмо упола мање меморије. Други начин да представимо матрицу у Луа је да уместо два индекса користимо један, који рачунамо на следећи начин:

mt = {} -- kreira se matrica
for i=1,N do
  for j=1,M do
    mt[(i-1)*M + j] = 0
  end
end

Ако су индекси случајно стрингови, заједнички индекс добија се надовезивањем стрингова убацујући између неки карактер, за који знамо да се неће наћи у њима, да их раздвоји. У традиционалним програмским језицима, представљање ретких графова матрицама повезаности узима доста меморије беспотребно. Начином имплементације, овај проблем је решен у Луа.

Повезане листе уреди

Пошто су табеле динамички објекти, лако је имплементирати повезане листе у Луа. Сваки чвор је представљен табелом и линкови су просто поља табеле која садрже референце на друге табеле. Нпр. за имплементацију базичне листе, где сваки чвор има два поља next и value, креирамо променљиву да буде корен листе.

list = nil

Да бисмо убацили елемент на почетак листе, са вредношчу v радимо следеће:

list = {next = list, value = v}

Наредни код илуструје пролазак кроз листу:

local l = list
while l do
  <visit l.value>
  l = l.next
end

Друге врсте листи, нпр. двоструко повезане или кружне, такође се могу лако имплементирати у Луа. Међутим, ретко се користе јер постоје ефикаснији начини за представљање података.

Изрази уреди

Изрази у Луа укључују нумеричке константе и стринговне литерале, промењливе, унарне и бинарне операције и позиве функција.

Такође, у изразе спадају и дефиниције функција и конструктори табела.

Аритметички оператори уреди

Луа подржава уобичајене аритметичке операторе: сабирање, одузимање, множење, дељење, експонент, модул, негација (+, -, *, /, %, ^, -).

Релациони оператори уреди

У Луа имамо следеће релационе операторе: < > <= >= == ~=, где је последњи набројани негација једнакости.

Логички оператори уреди

Логички оператори су анд, ор и нот.

Надовезивање уреди

Надовезивање се врши употребом две тачке (..). Уколико је неки од операнада типа нумбер, он се аутоматски конвертује у стринг. Пошто су стрингови имутабилни, свако надовезивање креира нови стринг без икаквих последица по операнде.

Наредбе[4] уреди

Наредбе доделе уреди

Под наредбом доделе подразумева се промена вредности променљиве или поља у табели. Луа подржава вишеструко додељивање, када се листи променљивих додељује листа вредности. Додељивање протиче наведеним редоследом.

a, b = 10, 2*x

Код вишеструког додељивања, Луа најпре рачуна вредности, па их потом додељује. Због тога се размена вредности променљивих лако кодира.

x, y = y, x -- swap 'x' for 'y'
a[i], a[j] = a[j], a[i] -- swap 'a[i]' for 'a[j]'

Луа увек прилагођава број вредности броју променљивих. Уколико у вишеструком додељивању има више променљивих него вредности, променљиве које су остале ускраћене за вредност од стране програмера, аутоматски ће добити вредност нил, док у случају да има више вредности него променљивих, вишак ће бити одбачен.

Локалне променљиве и блокови уреди

Осим глобалних, Луа подржава и локалне променљиве. Да је нека променљива локална, означава се стављањем кључне речи лоцал испред имена променљиве. Локалне променљиве су видљиве искључиво унутар блока у коме су декларисане. Под блоком подразумевамо тело неке петље, тело функције, фајл...

x = 10
local i = 1 -- local to the chunk
while i <= x do
    local x = i*2 -- local to the while body
    print(x) --> 2, 4, 6, 8, ...
    i = i + 1
end
if i > 20 then
    local x -- local to the "then" body
    x = 20
    print(x + 2) -- (would print 22 if test succeeded)
else
    print(x) --> 10 (the global one)
end
print(x) --> 10 (the global one)

Проблем настаје у интерактивном моду, када се извршава линија по линија. Решење је међутим да се блок стави између кључних речи до и енд.

Коришћење локалних променљивих спречава нагомилавање разних имена променљивих.

Контролне наредбе уреди

if then else уреди

Наредба if тестира да ли је услов испуњен и ако јесте извршава then -грану, а у супротном извршава else -грану, уколико она постоји. Да би се писали угњеждени if -ови користи се наредба elif, пошто Луа нема наредбу switch.

if op == "+" then
    r = a + b
elseif op == "-" then
    r = a - b
elseif op == "*" then
    r = a*b
elseif op == "/" then
    r = a/b
else
    error("invalid operation")
end

while, repeat i for уреди

Као и код других програмских језика Луа прво тестира услов wхиле наредбе и ако је услов испуњен онда се извршава тело петље, а ако услов није испуњен онда се извршавање тела while наредбе прекида.

local i = 1
while a[i] do
    print(a[i])
    i = i + 1
end

repeat наредба има слично значење као и у програмском језику паскал. Тело наредбе се извршава све док услов наредбе не постане задовољен.

-- print the first non-empty input line
repeat
    line = os.read()
until line ~= ""
print(line)

Луа поседује две врсте for наредби: нумерички for и генерички for. Нумерички for има следећу синтаксу:

for var=exp1,exp2,exp3 do
    <something>
end

Наредба for ће извршавати тело петље за вредности променљиве var које иду од exp1 до exp2 са кораком exp3. Генерички фор служи за пролзак кроз колекције.

break, return уреди

Наредбе break и return нам дозвољавају да искочимо из блока. Наредбу бреак користимо да прекинемо извршавање петље. Наредба break прекида извршавање for, while и repeat петљи и не може се користити изван њих, такодје она се мора налазити унутар блокова као последња наредба. После прекида извршавања петљи програм наставља са радом одмах након прекинуте петље.

local i = 1
while a[i] do
    if a[i] == v then break end
    i = i + 1
end

return наредба се користи да се прекине извршавање функција и да се врати вредност функције.

Функције[4] уреди

Функције су главни механизам за апстраховање наредби и израза у програмском језику Луа. Функције могу да извршавају специфичне задатке или да израчунавају и враћају вредности. У првом случају функцију користимо као наредбу, а у другом случају је користимо као израз.

print(8*9, 9/8)
a = math.sin(3) + math.cos(10)
print(os.date())

У оба случаја аргументи функције се налазе унутар заграда, заграде се морају писати и када функција нема аругмената које би добила.

Дефиниција функције у Луи има сличну синтаксу као и у другим програмским језицима.

function add (a)
    local sum = 0
    for i,v in ipairs(a) do
    sum = sum + v
end
return sum
end

Функција почиње са речи function па иде име функције праћено листом аргумената које се налази унутар заграда. Тело функције се завршава се речи енд. Ако функција враћа вредност неког израчунавања или желимо да прекинемо извршавање функције пре краја блока тада се унутар тела функције налази и реч return.

Стандардна библиотека[4] уреди

Библиотека за рад са математичким функцијама[5] уреди

Библиотека math садржи стандардне функције: тригонометријске (sin, cos, tan, asin, acos, ...), експоненцијалне (exp, log, log10), функције за заокруживање бројева (floor, ceil), max, min, функције за генерисање псеудо-случајних бројева (random, randomseed) али и променљиве pi и huge, која означава највећи представљив број. Све тригонометријске функције раде у радијанима. Можемо користити функције deg и rad унутар ове библиотеке за конвертовање у јединицу која нам одговара. Функцију math.random можемо позивати на три различита начина. Ако је позивамо без аргумената, враћа нам псеудо-случајан реални број са униформном расподелом на интервалу [0,1). Када је позивамо са једним параметром, целим бројем n, враћа нам псеудо-случајан цео број између 1 и n. I коначно, можемо је позвати са два аргумента, два цела броја, и она ће нам вратити псеудо-случајан цео број из интервала одређеног са тим бројевима. Ако поновимо позив ове функције math.random више пута, генерисаће нам стално исти низ бројева. То је добро када тестирамо неки програм, али лоше ако је то нпр. део неке игрице. Да би то спречили, користи се функција:

math.randomseed(os.time())

Функција os.time() нам враћа тренутно време, обично број секунди протекао од неке епохе.

Библиотека за рад са табелама уреди

У овој библиотеци налазе се функције за сортирање, уметање, брисање и надовезивање. Функција table.insert убацује елемент у низ, померајући друге елементе на слободна места. Нпр. ако је t низ {10, 20, 30}, након позива table.insert(t, 1, 15) добићемо као резултат низ {15, 10, 20, 30}. Ако позовемо ову функцију без навођења позиције, ставиће елемент на последње место.

Програм који чита линију по линију улаза, чувајући притом линије у неком низу:

t = {}
for line in io.lines() do
  table.insert(t, line)
end
print(#t) --> (broj pročitanih linija)

Функција table.remove уклања елемент са дате позиције у низу, померајући друге да се сложе и враћа скинути елемент. Уколико позиција није дата, скида последњи.

Са ове две функције прилично праволинијски се имплементриају стек, ред, двоструки ред. Ову структуру можемо иницијализовати са t={}. Операција push еквивалентна је са table.insert(t, x); операција pop еквивалентна је са table.remove(t). Позив table.insert(t, 1, x) додаје елементе на почетак и table.remove(t, 1) уклања елементе са краја. С обзиром да су ове функције имплементиране у C-у, њихови позиви нису много скупи, и довољно добро раде за низове од нпр. стотинак елемената.

Библиотека за рад са нискама[6] уреди

Луа интерпретер као засебна јединица има веома ограничену подршку за рад са нискама. Његове могућности заустављају се на формирању литерала, њиховом спајању, и рачунању дужине ниске. Библиотека за рад са нискама пружа далеко веће могућности. Основне могућности библиотеке за ниске су једноставне трансформације и издвајање информација из ниски. Функције string.tolower(s) и string.toupper(s)- враћају копије ниске s, у којој су сва слова трансформисана у мала, односно велика слова. Функција string.sub(s, i, j)- враћа подниску ниске s која се налази од и-те до ј-те позиције, укључујући и карактер на ј-тој позицији. Пример једноставног коришћења основних функција:

niska = "[Zdravo, Svete!]"
podniska = string.sub(niska, 2, -2)
print(podniska) --> "Zdravo, Svete!"
mniska = string.tolower(podniska)
print(mniska) --> "zdravo, svete!"
vniska = string.toupper(podniska)
print(vniska) --> "ZDRAVO, SVETE!"

Осим тога, ова библиотека садржи и могућност форматирања ниски приликом штампања, као и напредне механизме за тражење образаца у нискама (функције gsub, find, match), који се не заснивају ни на ПОСИX, ни на Перл регуларним изразима. Имплементација се врши у мање од 500 линија кода, и досег могућности је мањи од претходно поменутих имплементација, које заузимају знатно више меморије.

Библиотека за улаз и излаз уреди

Једноставан I/О модел уреди

Једноставан модел за улаз и излаз заснива се на две датотеке, стандардном улазу и стандардном излазу. Све операције које се примењују у функцијама io.read и io.write односе се на ове две датотеке, које се могу променити позивима функција io.input и io.output. Пример коришћења једноставног I/О модела:

linije = {}
for linija in io.lines() -- io.lines() čita sve linije sa standardnog ulaza.
   do linije[#linije + 1] = linija
end
for linija in linije
   io.write(linija, "\n") -- io.write() štampa na glavni izlaz
end

Комплетан I/О модел[7] уреди

Комплетан I/О модел заснован је на принципу објекта типа ФИЛЕ* из C-а. Објекат овог типа може се добити позивом функције io.open, чији су аргументи путања до датотеке коју треба да отворимо, и ниска која садржи начин на који желимо да отворимо датотеку. Уколико датотека непостоји, враћа се nul. Након успешног отварања фајла, на њега се могу применити исте функције као и на стандардни улаз и излаз, али се користи објектна синтакса. Датотеке треба затворити пре завршетка програма, позивањем функције close. Пример:

datoteka = io.open("datoteka.dat", "r")
t = datoteka:read("*all") --čita sve linije iz datoteke u tabelu
datoteka.close()

Библиотека системских функција[8] уреди

Библиотека оперативног система садржи функције за манипулацију датотекама и фасциклама, тренутни датум и време, као и функције уже везане за оперативни систем.

Датум и време уреди

Две функције у библиотеци оперативног система намењене су прибављању информација о тренутном датуму и времену, као и форматирању и трансформацији различитих облика представљања датума и времена. Функција os.time, позвана без аргумената, враћа тренутно време кодирано као број (на већини оперативних система Униx Тиме). Ако се проследи табела као аргумент, ова функција чита одговарајуће кључеве у табели, који треба да представљају годину, месец, датум, сат, минут, секунд, и враћа их кодиране као број. Прва три кључа (година - "year", месец - "month" и датум - "day") су обавезна, а ако неки од преостала три кључа недостају, за њихову вредност узеће се 12 (у случају сата) или 0 (минути и секунде). Функција date, упркос свом имену, најбоље се може описати као супротна функција од функције time. Ова функција враћа табелу са одговарајућим кључевима, који одговарају години, месецу, датуму, сату, минуту и секунди, на основу ниске и броја који су прослеђени као аргументи (ниска одговара формату у који желимо да претворимо датум). Пример:

sada = os.time()
print(sada) --> trenutno vreme u vidu broja sekundi koje su prošle od 1. januara 1970.
sada_tabela = os.date(sada)
print(sada_tabela["year"]) --> trenutna godina
januar = {year=2018, month = 1, day = 12, hour = 0}
januar_unix = os.time(januar)
print(januar_unix) --> broj sekundi između 1. januara 1970. u ponoć, i 12. januara 2018. u podne
januar_tabela = os.date("*t", januar_unix)
print(januar_tabela["day"]) --> 12

Функција os.clock може да се користи за мерење перформанси процесора. Она враћа време протекло од почетка извршавања програма у секундама.

Остале системске функције уреди

os.exit() прекида програм. os.execute(command) извршава команду на нивоу оперативног система.

Пример кода уреди

Класични "Поздрав свете" програм:

print("Pozdrav svete!")

или:

io.write("Pozdrav svete!\n")

принт() додаје карактер за прелаз у нови ред, за разлику од ио.wрите() где се он мора додати у самом коду.

-- Komentar u Lui počinje sa duplom crticom i završava sa krajem linije.
-- [[Višelinijski stringovi i komentari
 se obeležavaju duplim uglastim zagradama.]]

Факторијел је пример рекурзивне функције:

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

Други облик за функцију факторијела потиче од Луиног начина израчуна логичког оператора, по коме Луа враћа вредност последње израчунатог операнда у изразу:

function faktorijel2(n)
  return n == 0 and 1 or n * faktorijel2(n - 1)
end

Види још уреди

Референце уреди

  1. ^ Луа 5.3 Референце Мануал - цонтентс
  2. ^ пyтхон - Wхy ис Луа цонсидеред а гаме лангуаге? - Стацк Оверфлоw
  3. ^ Курт Јунг, Аарон Броwн, Бегиннинг Луа Программинг(1ст Едитион),Бирмингхам:Wроx. 2007. ISBN 978-0-470-06917-2.
  4. ^ а б в Роберто Иерусалимсцхy, Программинг ин Луа(Сецонд Едитион), Луа.орг. 2006. ISBN 978-85-903798-2-9.
  5. ^ Луиз Хенриqуе де Фигуеиредо,Wалдемар Целес, Луа Программинг Гемс,Луа.орг. 2008. ISBN 978-85-903798-4-3.
  6. ^ Јордан Кауфман, ЛУА Сцриптинг Маде Ступид Симпле,ЦреатеСпаце Индепендент Публисхинг Платформ, 2017,ISBN 978-1-5193-2259-3.
  7. ^ Митцхелл Барнетт, Луа: Qуицк Референце,Фоицица.цом. 2017. ISBN 978-0-9912379-3-7.
  8. ^ Марио Касуба, Луа Гаме Девелопмент Цоокбоок, Пацкт Публисхинг. 2015. ISBN 978-1-84951-550-4.

Спољашње везе уреди