Lua

Ajalugu

Lua programmeerimiskeele lõid 1993. aastal Roberto Ierusalimschy, Luiz Henrique de Figueiredo ja Waldemar Celes ning Computer Graphics Technology Group (Techgraf) liikmed Rio de Janeiro Piiskoplikus Katoliku Ülikoolis (Pontifical Catholic University of Rio de Janeiro) Brasiilias.
Aastatel 1977–1992 kehtisid Brasiilias arvutitarkvarale ja -riistvarale tugevad kaubandustõkked. Nende piirangute tõttu ei olnud Tecgrafi klientidel võimalik (ei poliitilistel ega rahalistel põhjustel) osta kohandatavat tarkvara välismaalt. Nende asjaolude tõttu implementeeris Tecgraf põhilised vahendid iseseisvalt.
Lua ajalooline "isa ja ema" olid andmete kirjeldus/konfiguratsiooni keel SOL (Simple Object Language) ja DEL (Data-Entry Language). Neid on iseseisvalt arendanud Tecgraf aastatel 1992–1993, lisades paindlikkust kahte projekti (mõlemad olid interaktiivsed graafilised programmid ettevõtte Petrobras rakenduste jaoks). Neis oli puudus igasugustest voo reguleerimise struktuuridest SOL-is ja DEL-is ja Petrobras tundis kasvavat vajadust lisada rakendustesse täiendavaid programmeeritavaid võimalusi.
Lua 1.0 oli disainitud nii, et selle objekti konstruktorid, olles nii vähesel määral erinevad praegusest kergusest ja paindlikkuse laadist, inkorporeeritud SOLi andmekirjelduse süntaksiga (siit tuleneb ka nimi "Lua". Sol tähendab portugali keeles 'päikest', seega sobis arendatava keele nimeks sõna "Lua" - 'Kuu'). Lua kontrollstruktuuride süntaksist oli enamus laenatud Modulast (if, while, repeat/until), aga samuti oli võetud mõjutusi CLU-st (korduvad ülesanded ja korduv tagastamine funktsioonide väljakutsumisel kui lihtsam alternatiiv viitparameetritele või kaudsetele viitadele), C++ keelest (" lokaalse muutuja deklareerimine ainult seal, kus me vajame seda), SNOBOL-ist ja AWK-st (assotsiatiivne massiiv). Artiklis, mis avaldati Dr. Dobb's Journalis, väitsid Lua keele loojad, et LISP ja Scheme kui nende ainsad üldlevinud andmete struktureerimise mehhanismid (list) olid tugevalt mõjutatud nende otsusest arendada tabelit kui tähtsaimat Lua andmestruktuuri.
Lua semantika on enim mõjutatud Scheme poolt, eriti seoses anonüümsete funktsioonide kasutuselevõtuga ja täieliku sõnastiku kindlaksmääramisega.
Lua versioonid enne versiooni 5.0 olid välja lastud BSD-litsentsiga sarnase litsentsi alusel. Alates versioonist 5.0 on Lua olnud MIT litsentsi all. Mõlemad on leebed vaba tarkvara litsentsid ja peaaegu identsed.

Funktsionaalsus

Luad on tavaliselt kirjeldatud kui "mitmeparadigmalist" keelt, toetades väikest hulka üldisi funktsioone, millega saab laiendada sobivust eri tüüpi probleemidele, selle asemel et toetada palju keerulisemat ja ranget spetsifikatsiooni, sobitamaks ühte paradigmat. Näiteks Lua ei sisalda ilmset päriluse tuge, aga lubab seda implementeerida koos metatabelitega. Lisaks lubab Lua programmeerijal implementeerida nimeruume, klasse ja teisi seotud funktsioone, kasutades selle üksiku tabeli implementeerimist. Esimese klassi funktsioonid, mis võimaldavad töötamist paljude funktsionaalse programmeerimise tehnikatega ja täielik staatiline skoopimine võimaldavad tihedat informatsiooni peitmist, täites väiksema privileegi printsiipi.
Üldiselt püüab Lua tagada lihtsust ning paindlikke metafunktsioone, mis võivad olla vajadusel sisseehitatud, selle asemel et varustada funktsiooni seadistamise spetsiifikaga üht programmeerimisparadigmat. Tulemuseks olev baaskeel on kerge – interpretaatori maht on kompileeritult ainult umbes 180 kB ning on lihtsasti kohandatav laiale rakenduste valikule.
Lua keel võimaldab dünaamilist tüüpimist ja on mõeldud kasutamiseks kui keelelaiendus või skriptimiskeel ning on piisavalt kompaktne, et sobib erinevate platvormidega. See toetab ainult väikest osa elementaarseid andmestruktuure nagu tõeväärtuse väärtused, numbrid (kahekohalise täpsusega ujukomaarvud tavaliselt) ja sõned. Enamlevinud andmestruktuure nagu massiive, hulki, liste ja kirjeid saab taasesitada Lua ainsat omaenda andmestruktuuri – tabelit – kasutades, Tabel näol on tegu põhimõtteliselt heterogeenne assotsiatiivse massiiviga.
Lua implementeerib väikest hulka kõrgetasemelisi funktsioone nagu esimese klassi funktsioonid, "prügikorjajad" (garbage collection), sulundid, sabakutse (tail call), tüübiteisendus (automaatne muutmine sõne ja numbri vahel), kaasprogrammid (ühise multitegumitöötlusega) ja dünaamiline mooduli laadimine.
Sisaldades ainult minimaalset hulka andmetüüpe, Lua püüdis saavutada tasakaalu jõudluse ja mahu vahel.

Koodi näidis

Klassikalist programmi "Tere maailm" saab kirjutada järgmise koodiga:
print("Tere maailm!")
Seda sama programmi saab kirjutada ka sedamoodi:
io.write('Tere maailm!\n')
Seda programmi saab luua ka nii nagu Lua veebilehel.
io.write("Hello world, from ", _VERSION, "!\n")
Kommentaarides kasutatakse järgmist süntaksit, mis sarnaneb programmeerimiskeelte Ada, Eiffeli, Haskelli, SQL-i ja VHDL-i omadega:
--Kommentaar keeles Lua algab kahe sidekriipsuga ja on kuni rea lõpuni.--[[ Mitmerealised sõned ja kommentaarid     on ümbritsetud kahekordsete kandiliste sulgudega. ]]--[=[ Seda tüüpi kommentaaridesse on teine --[[kommentaar]] pesastatud. ]=]
Faktoriaali funktsioon on implementeeritud, nii nagu järgmises näites näidatud.
function factoriaal(n)
  local x = 1
  for i = 2, n do
    x = x * i
  end
  return xend

Tsüklid

Lua keeles on nelja tüüpi tsükleid: while-tsükkel, repeat tsükkel (sarnane do-while tsükliga), numbriline for tsükkel ja üldine for tsükkel.
--tingimus = truewhile tingimus do
  --käsudendrepeat
  --käsuduntil tingimusfor i = esimene,viimane,samm do     --samm võib olla negatiivne, lubades for-tsüklil loendada alla või üles. Samm näitab, kui palju on järgmine väärtus eelmisest suurem. Näiteks kui samm on 1, i esimene väärtus on 1, siis järgmine on 1+1=2
  --käsud
  --näiteks väljastame i väärtuse: print(i)end
Üldine for-tsükkel:
for key, value in pairs(_G) do
  print(key, value)end
Üldine for-tsükkel itereerib üle tabeli _G, kasutades standardset iteraatori funktsiooni pairs, kuni see tagastab tühiväärtuse null.

Funktsioonid

Lua funktsioonikäsitluses on esimese klassi funktsioonide väärtused, mis on näidatud järgnevas näites, kus print-funktsiooni käitumine on modifitseeritud:
do
  local oldprint = print
  -- varu praeguse print funktsioon kui oldprint
  function print(s)
    --[[ Defineeri uuesti print function, tavalist print funktsiooni saab endiselt kasutada         läbi oldprint. Uuel on ainult üks argument]]
    oldprint(s == "foo" and "bar" or s)
  endend
Iga tulevane print-käsu väljakutse hakkab nüüd olema suunatud läbi uue funktsiooni ja tulenevalt Lua staatilisest skoopimisest on vana print funktsioon kättesaadav ainult uue modifitseeritud print jaoks.
Lua samuti toetab sulundeid, mida on demonstreeritud järgmises koodis:
function addto(x)
  --tagastab uue funktsiooni, mis lisab x-i argumendiks
  return function(y)
    --[=[ kui me viitame muutujale x, mis on väljaspool seda skoopi ja mille         eluperiood on lühem kui sellel anonüümsel funktsioonil,          Lua loob sulundi.]=]
    return x + y
  endendfourplus = addto(4)print(fourplus(3))  -- väljastab 7--Seda saab saavutada ka funktsiooni järgmisel viisil välja kutsudes:print(addto(4)(3))--[[ See on nii, sest me oleme kutsumas tagastatud funktsiooni `addto(4)'  koos argumendiga `3' otse.     See aitab ka vähendada andmehinda ja tõsta kasutust, kui oleme kutsunud iteratiivselt.]]
Muutuja x uus sulund on loodud igal korral, kui addto kutsutakse välja, nii et iga uus anonüümne funktsioon naaseb alati juurdepääsuga oma x-parameetrile. Sulundit haldab Lua "prügivedaja" just nagu iga teist objekti.

Tabelid

Tabelid on kõige tähtsamad andmestruktuurid (ja disainilt ainus sisseehitatud liitandmetüüp) Lua keeles ning on alus kõigile kasutaja loodud tüüpidele. Need sarnanevad kontseptuaalselt assotsiatiivse massiiviga PHP-s, sõnastikuga Pythonis ja räsidele Rubys või Perlis.
Tabel on kogum võtmete ja andmete paaridest, kus andmetele viitavad võtmed. Teiste sõnadega on see räsitud heterogeenne assotsiatiivne massiiv. Võti (indeks) võib omada iga väärtust, aga mitte NaN. Numbrilist võtit 1 peetakse erinevaks sõnelisest võtmest "1". i
Tabeleid luuakse kasutades {} konstruktorit:
a_table = {} -- Loome uue tabeli nimega a_table
Tabelid saavad viidata ka üksteisele:
a_table = {x = 10}  -- Loome uue tabeli koos ühe kirje kujutusega, kus "x" on seotud 10-ga.print(a_table["x"]) -- Väljastame sõnelise võtmega seotud väärtuse, mis on praegusel juhul 10.b_table = a_tableb_table["x"] = 20   -- Väärtuse muutmine tabelis. Uus väärtus on 20.print(b_table["x"]) -- Väljastame 20.print(a_table["x"]) -- Samuti väljastab 20, sest a_table ja b_table mõlemad viitavad samale tabelile.

Kirje

Tabelit on tihti kasutatud nagu struktuuri (või kirjet),kus on kasutatud sõnesid kui võtmeid, sest selline kasutus on väga tavaline. Lua funktsioonid on spetsiaalse süntaksiga, tegelemiseks selliste väljadega. Seda näitab järgmine koodinäide:
point = { x = 10, y = 20 }   -- Loome uue tabeli.print(point["x"])            -- Väljastame 10.print(point.x)               -- See rida on enamvähem sama, mis eelmine, kuid lihtsam lugeda.
                             -- .
Tsiteerides Lua 5.1 manuaali:
"Süntaks var.Name on süntaktiline suhkur süntaksist var['Name'];"

Nimeruum

Seotud funktsioonide salvestamiseks kasutatavaid tabeleid saab näidatu kui nimeruumi.
Point = {}Point.new = function(x, y)
  return {x = x, y = y}  --  return {["x"] = x, ["y"] = y}endPoint.set_x = function(point, x)
  point.x = x  --  point["x"] = x;end

Massiiv

Numbrilise võtme kasutamise korral sarnaneb tabel massiivi andmetüübiga. Lua massiivid on 1-baasil. Erinevalt paljudest programmeerimiskeeltest, kus esimene indeks on 0, on siin esimene indeks 1 (kuigi on lubatud kasutada ka indeksit 0).
Lihtne sõnemassiiv:
array = { "a", "b", "c", "d" }   -- Indeksid määratakse automaatseltprint(array[2])                  -- Väljastab  "b". Automaatne indekseerimine Luas hakkab 1-st.print(#array)                    -- Väljastab 4.  # See on pikkuse määramise operaator tabelitel ja sõnedel.array[0] = "z"                   -- 0 on legaalne indeks.print(#array)                    -- Jätkuvalt väljastab 4, Lua massiivid on 1-põhised.
Tabeli t pikkus on defineeritud olema iga täisarvuline indeks n nii, et t[n] ei ole tühi ja t[n+1] on tühi; veel enam, kui t[1] on tühi, siis n on 0. Regulaarse massiivi üks mitte-null väärtustest antakse muutujale n, Selle pikkus on sel juhul täpselt n ehk viimase indeksi väärtus. Kui massiivil on "auke" (tühjad väärtused on mittetühjade väärtuste vahel), siis #t saab olla iga indeks, mis on vahetult eelnev tühjale väärtusele (see tähendab, et ta võib iga sellist tühja väärtust pidada massiivi lõpuks).
Kahedimensiooniline tabel:
ExampleTable ={
     {1,2,3,4},
     {5,6,7,8}}print(ExampleTable[1][3]) -- Väljastab "3"print(ExampleTable[2][4]) -- Väljastab "8"
Objektide massiiv:
function Point(x, y)        -- "Point" objekti konstruktor
  return { x = x, y = y }   -- Loome ja tagastame uue objekti (tabel)endarray = { Point(10, 20), Point(30, 40), Point(50, 60) }   -- Loome punktide massiivi
                        -- massiiv = { { x = 10, y = 20 }, { x = 30, y = 40 }, { x = 50, y = 60 } };print(array[2].y)                                         -- Väljastab 40
Kasutades kujutust normaalse massiivi järgitegemiseks on aeglasem kui kasutada seda tegeliku massiivina. Siiski on Lua tabelid optimeeritud kasutama neid nagu massiive.

Metatabelid

Laiendatud semantikas on Lua võtmefunktsioon ja metatabeli kontseptsioon lubab Lua tabeleid kohandada võimsaimasse olekusse. Järgnev näide demonstreerib "lõpmatut" tabelit. Iga , fibs[n] annab -inda Fibonacci jada liikme kasutades dünaamilist programmeerimist ja tabeli pidamist, mis on optimeerimise tehnika.
fibs = { 1, 1 }                                -- esialgne väärtus fibs[1]'le ja fibs[2]'le.setmetatable(fibs, {
  __index = function(values, n)                --[[ __index on funktsioon, mis on eeldefineeritud Lua poolt,                                                    See kutsutakse välja, kui võtit "n" ei eksisteeri. ]]
    values[n] = values[n - 1] + values[n - 2]  -- Calculate and memoize fibs[n].
    return values[n]
  end})

Objektorienteeritud programmeerimine

Lua keeles ei ole sisseehitatud klasse, kuid sellegi poolest on võimalik objektorienteeritult programmeerida. Selleks tuleb kasutada kaht Lua keele funktsiooni: esimese klassi funktsioone ja tabeleid. Objekti saab luua funktsioonide ja soetud andmete tabelisse paigutamisega. Metatabeli mehhanismi abil saab implementeerida pärilust.
Luas ei ole sellist mõistet nagu "klass" koos nende tehnikatega: pigem on kasutusel prototüübid nagu programmeerimiskeeltes Self ja JavaScript. Uusi objekte luuakse nii tehase meetodiga (see konstrueerib uued objektid nullist) kui ka kloonides eksisteerivat objekti.
Lua toetab mõnda süntaktilist suhkrut, et aidata kaasa objekti orientatsioonile. Deklareerides liikmefunktsiooni prototüübi tabelisse sisse, saab kasutada koodi function table:func(args), millega on samaväärne kood function table.func(self, args). Klassimeetodeid saab samuti kutsuda kooloni abil. Kood object:func(args) on samaväärne koodiga object.func(object, args).
Loome tavalise vektori objekti:
local Vector = {}Vector.__index = Vectorfunction Vector:new(x, y, z)    -- konstruktor
  return setmetatable({x = x, y = y, z = z}, Vector)endfunction Vector:magnitude()     -- teine meetod
  -- viitab varjatud objektile kasutades iseennast
  return math.sqrt(self.x^2 + self.y^2 + self.z^2)endlocal vec = Vector:new(0, 1, 0) -- Loome vektoriprint(vec:magnitude())          -- Kutsume välja (output: 1)print(vec.x)                    -- Ligipääs mälus olevale väärtusele (output: 0)

Sisemus

Lua programmid ei ole otseselt interpreteeritud Lua tekstifailidest, aga on kompileeritud baitkoodi, mis töötab Lua virtuaalmasinas. Kompileerimise protsess on tüüpiliselt nähtamatu kasutajale ja on sooritatud jooksutamisaja kestel, aga seda saab teha ühenduseta, et kasvatada laadimiskiirust või vähendada rakenduse keskkonna mälu "jalajälge" kompilaatorit välja jättes. Lua baitkood saab samuti olla produtseeritud ja täidetud Luaga kasutades minemaviskamise funktsiooni sõne teegist ja load/loadstring/loadfile funktsioone. Lua versioon 5.3.3 on implementeeritud umbes 24 000 rea C-keele koodiga. 
Nagu enamus keskprotsessoreid (CPU) ja erinevalt enamikust virtuaalmasinatest, mis on kuhjapõhised, Lua virtuaalmasin on registripõhine ja järelikult palju enam sarnane päevakajalise riistvara disainiga. Registri arhitektuur väldib nii ülemäärast väärtuste kopeerimist kui ka vähendab kogu instruktsioonide arvu funktsiooni kohta. Lua 5 virtuaalmasin on üks esimesi puhtalt registripõhiseid virtuaalmasinaid omades laia kasutust. Perli Parrot ja Androidi Dalvik on kaks teist teada-tuntud registripõhist virtuaalmasinat.
See näide on baitkoodi list faktoriaali funktsioonist, mis sai defineeritud ülal (nagu näha luac 5.1 kompilaatori poolt):
function <factorial.lua:1,7> (9 instructions, 36 bytes at 0x8063c60)
1 param, 6 slots, 0 upvalues, 6 locals, 2 constants, 0 functions
1[2]LOADK 1 -1; 1
2[3]LOADK 2 -2; 2
3[3]MOVE  3 0
4[3]LOADK 4 -1; 1
5[3]FORPREP 2 1; to 7
6[4]MUL  1 1 5
7[3]FORLOOP 2 -2; to 6
8[6]RETURN 1 2
9[7]RETURN 0 1

C API

Lua on kavandatud olema sisseehitatud teistese rakendustesse ja C API toetab seda eesmärki. API on jagatud kaheks osaks: Lua südamikuks ja Lua abiteegiks.
Lua API disain elimineerib vajaduse manuaalseks viidete haldamiseks C koodis, erinevalt Pythoni API-st. Keeltel nagu see on API minimalistlik. Arenenud funktsionaalsus on toetatud abiteegi poolt, mis koosneb suurel määral eeltöödeldud makrodest, mis abistavad koos liittabeli operatsioonidega.

Kuhi

Lua C API põhineb kuhjal. Lua tagab funktsioonide tõukamist ja esiletoomist enamikus lihtsates C andmetüüpides (näiteks täisarvud, reaalarvud ja nii edasi) ning kuhjas. Lisaks ka funktsioonid töötlevad tabeleid läbi kuhja. Lua kuhi erineb mõnevõrra traditsioonilisest kuhjast. Kuhi saab olla indekseeritud otse. Negatiivsed indeksid kalduvad kõrvale kuhja ülemisest otsast. Kui −1 on üleval (viimati uuendatud väärtus), kuna positiivsed indeksid annavad märku nihkest alumises otsas (vanad väärtused).
C ja Lua vaheliste andmete kogumise funktsioonid on samuti tehtud kasutades kuhja. Kutsudes Lua funktsiooni, argumendid on surutud kuhja peale ja siis lua_call on kasutatud kutsumaks tegelikku funktsiooni. Kui C-keeles kirjutatud funktsiooni otseselt kutsuda Lua keeles, siis argumendid on loetud kuhjast.

Näide

Siin on näide sellest, kuidas kutsuda välja Lua funktsiooni C-keeles.
#include <stdio.h>#include <lua.h> //Lua main library (lua_*)#include <lauxlib.h> //Lua auxiliary library (luaL_*)int main(void){
    //loome Lua seisundi
    lua_State *L = luaL_newstate();

    //lae ning täida string
    if (luaL_dostring(L, "function foo (x,y) return x+y end")) {
        lua_close(L);
        return -1;
    }

    //lükka globaalne väärtus "foo" (funktsioon on eelnevalt defineeritud)
    //kuhja, jälgitud täisarvude 5 ja 3 poolt.
    lua_getglobal(L, "foo");
    lua_pushinteger(L, 5);
    lua_pushinteger(L, 3);
    lua_call(L, 2, 1); //kutsu funktsioon koos kahe argumendiga ja tagasta üks väärtus.
    printf("Result:%d\n", lua_tointeger(L, -1)); //väljastame väärtuse
    lua_close(L); //sulgeme lua seisundi.
    return 0;}
Näite jooksutamine annab: 
$ cc -o example example.c -llua
$ ./example
Result: 8

Spetsiaalsed tabelid

C API samuti tagab mõned spetsiaalsed tabelid lokaliseeritult mitmesuguste "pseudo-indeksitega" Lua kuhjas. LUA_GLOBALSINDEX enne Lua 5.2 on üldises tabelis. _G on peamine nimeruum. Seal on samuti registriga määratud LUA_REGISTRYINDEX, kus C programmid saavad salvestada Lua väärtusi hilisema vajaduse jaoks.

Laiendus ja seos

Lua API-t kasutades on võimalik kirjutada laiendmooduleid. Laiendmoodulid on jagatud objektid, mis on harjunud laiendama interpreteerija funktsionaalsust, tagades algupärase hõlpsuse Lua skriptidele. Selline moodul ilmub, kui nimeruumi tabelis hoitakse vastavaid funktsioone ja muutujaid. Lua skriptid võivad laadida laiendusmooduleid kasutades require, just nagu moodulid oleks kirjutatud Lua keeles endas.
Kasvav moodulite kollektsiooni teatakse kui "rocks", mis on saadaval paketihaldussüsteemi LuaRocks kaudu, CPAN vaimus, RubyGems, Python Eggs.
Eelkirjutatud Lua seosed eksisteerivad enamikus populaarsetes programmeerimiskeeltes, sealhulgas skriptimiskeeltes nagu C++. Seal on mitu mallipõhist juurdepääsu ja mõni automaatne seosegeneraator.

Videomängud

Videomängude arenduses on Lua mängude programmeerijate poolt laialdaselt kasutatud kui skriptimiskeel ehk tänu selle tajutud lihtsuses manustamisel, kiirel täitmisel ja lühikesel õppimiskõveral.
2003. aastal korraldatud GameDev.neti küsitlus näitas, et Lua on kõige populaarsem skriptimiskeel mängude programmeerimisel. Lua kuulutati 12. jaanuaril 2012 Front Line Award 2011 võitjaks ajakirjas Game Developer kategoorias "Programmeerimisvahendid".