Matatablice
Każda wartość w Lua posiada metatabelę. Metatabela jest zwykłą tabelą Lua, która definiuje zachowanie oryginalnej wartości pod kątem specjalnych operacji. Możliwa jest zmiana szeregu aspektów zachowania wartości przez ustawienie odpowiednich pól metatabeli. Dla przykładu, kiedy wartość nienumeryczna jest operandem dodawania, Lua sprawdza metatabelę tej wartości pod kątem pola __add
. Jeżeli znajdzie, to wywoływana jest ta funkcja aby przeprowadzić operację dodawania.
Klucze metatabeli nazywane są zdarzeniami a ich wartości metametodami. W poprzednim przykładzie zdarzeniem jest "add" i metametodą jest funkcja wykonująca dodawanie.
Dostęp do metatabeli dowolnej wartości możliwy jest przez wywołanie funkcji getmetatable. Możliwa jest podmiana metatabeli tabel przy pomocy funkcji setmetatable. Nie jest możliwa zmiana metatabel dla innych typów Lua (wyjątek stanowi użycie biblioteki debug), w takim przypadku należy użyć API w języku C.
Poniższy przykład demonstruje "nieskończoną" tablicę, która dla kolejnych indeksów zwróci wartość ciągu fibonacciego na danej pozycji. Taki efekt został osiągnięty przez modyfikacje metatablicy - zdefiniowanie metametody dla klucza __index
.
fibs = { 1, 1 }
setmetatable(fibs, {
__index = function(values, n)
values[n] = values[n - 1] + values[n - 2]
return values[n]
end
})
Wszystkie dostępne metametody:
Metametoda | Opis |
---|---|
__index(table, index) |
Wywoływana, gdy następuje odwołanie tablicy za pomocą indeksu. |
__newindex(table, index, value) |
Wywoływana, gdy następuje przypisanie do elementu o danym indeksie. |
__call(table, ...) |
Uruchamiana, gdy tablica jest wywołana jak funkcja |
__add(table, value) |
Operator dodawania "+". |
__sub(table, value) |
Operator odejmowania "-". |
__mul(table, value) |
Operator mnożenia "*". |
__div(table, value) |
Operator dzielenia "/". |
__mod(table, value) |
Operator reszty z dzielenia "%". |
__pow(table, value) |
Operator potęgowanie "^". |
... |
... |
Programowanie obiektowe
Lua nie posiada natywnego wsparcia do progrmowania obiektowego, jaki znami na przykład z języla C++. Posiada jednak pewien lukier syntaktyczny, który umożliwa emulowanie OOP. Na przykład możemy utworzyć tablicę, Vector, którą będzie pełniła rolę klasy. Następnie definiujemy metody tej klasy w następujący sposób: Vector:nazwa_metody(argumenty...)
.
Jeżeli chcemy odwołać się do obiektu, na rzecz którego wywoływana jest metoda, należy skorzystać z referencji self
.
Mechanizm dziedziczenia występuje poprzez ustawienie zdarzenia __index
danej klasy na klasę narzędną.
local Vector = {}
Vector.__index = Vector
function Vector:new(x, y, z) --konstruktor
return setmetatable({x = x, y = y, z = z}, Vector)
end
function Vector:magnitude() --metoda
return math.sqrt(self.x^2 + self.y^2 + self.z^2)
end
local vec = Vector:new(0, 1, 0) --utworzenie nowego obiektu vec
print(vec:magnitude()) --wywołanie metody magnitude
print(vec.x) --dostęp do atrybutu x
Domknięcia
Domknięcie jest kombinacją funkcji i leksykalnego środowiska w którym ta funkcja została zdeklarowana. To środowisko zawiera każdą zmienną lokalną która była w zasięgu w momencie kiedy domknięcie zostało stworzone
function newCounter ()
local i = 0
return function () --funkcja anonimowa
i = i + 1
return i
end
end
c1 = newCounter()
print(c1()) --> 1
print(c1()) --> 2