Obsah
1. Demonstrační příklad – podpora bitmapové grafiky pro Lua skripty
2. Testovací skripty využívající rozhraní pro bitmapovou grafiku
2.1 Vykreslení jednoduchého obrazce založeného na aliasu
2.2 Vykreslení Mandelbrotovy množiny
2.3 Napodobení demo efektu „plasmy“
2.4 RGB spirála
2.5 Animace – amplitudová modulace (AM)
3. Objektově orientované programování v jazyku Lua
4. Vlastnosti asociativních polí
5. Vytvoření objektu pomocí uzávěru
6. Deklarace metod vně konstruktoru
7. Alternativní způsob zápisu metod
8. Odkazy na Internetu
9. Obsah další části seriálu
1. Demonstrační příklad – podpora bitmapové grafiky pro Lua skripty
V předchozí části seriálu o programovacím jazyce Lua jsme si ukázali, jakým způsobem je možné v aplikaci využívající interpretr Lua provést registraci céčkové funkce tak, aby ji bylo možné volat z Lua skriptů. Také jsme si popsali některé funkce aplikačního programového rozhraní (API) interpretru, jenž slouží k získání parametrů předávaných z Lua skriptu céčkové funkci, včetně kontroly počtu skutečně předaných parametrů a jejich typu. Tyto znalosti využijeme v dnešním demonstračním příkladu, který je poněkud rozsáhlejší, než příklady předchozí. Jedná se o aplikaci se zabudovaným interpretrem jazyka Lua, který je rozšířený o několik funkcí sloužících pro tvorbu bitmapových (rastrových) obrázků ukládaných do souborů typu PPM – Portable Pixel Map. Tento příklad je – po dalším rozšíření, zejména přidání podpory kreslení čar, výplní a textů – využitelný pro výuku základů počítačové grafiky (ostatně právě z tohoto důvodu pro potřeby škol vznikl). V aplikaci jsou zaregistrovány čtyři céčkové funkce, které je možné z Lua skriptů volat:
Název funkce | Parametry | Význam |
---|---|---|
createBitmap | width, height | Vytvoření bitmapy se zadanou šířkou a výškou |
clearBitmap | – | Vymazání celé bitmapy černou barvou |
saveBitmap | filename | Uložení bitmapy do externího souboru se specifikovaným jménem |
putpixel | x, y, r, g, b | Vybarvení pixelu na souřadnicích [x,y] barvou [r,g,b] |
Vzhledem k tomu, že se jedná o aplikaci navrženou pro školní prostředí s relativně omezenou výkonností počítačů (popis některých počítačových učeben s darovanými počítači a způsob jejich administrace by vydal na samostatný článek), je možné pracovat vždy pouze s jednou bitmapou, jejíž maximální rozměry jsou 1024×1024 pixelů, což při formátu uložení 24 bitů na pixel představuje oblast paměti o velikosti 3 MB. Při volání všech čtyř zmíněných funkcí se kontroluje jak počet parametrů, tak i jejich typ a rozsah – například souřadnice vybarvovaného pixelu musí ležet uvnitř rastrového obrázku. V případě, že parametry nejsou zadány korektně či dojde k jinému chybovému stavu (pokus o uložení bitmapy, která ještě nebyla vytvořena atd.), je na zásobník interpretru jazyka Lua uložena chybová zpráva, skript je ukončen a následně je vypsán aktuální stav zásobníku, který obsahuje mj. i chybovou zprávu (může se samozřejmě jednat i o zprávu vytvořenou vlastním interpretrem, například ve chvíli, kdy skript obsahuje syntaktickou chybu).
Zdrojový kód aplikace má velikost cca 6 kB, přičemž pro zjednodušení zápisu programu je využíváno několik maker (LUA_ERROR, NUMBER_OF_PARAMETERS, NUMBERP, CHECK_RANGE a další), takže přidání a registrace dalších céčkových funkcí pro práci s rastrovou grafikou je snadné:
/*
* Čtvrtý demonstrační příklad
*
* Základní podpora pro práci s bitmapovou grafikou v jazyce Lua.
*
* Autor: Pavel Tišnovský, 2009, lze šířit v souladu s GPL
*/
#include <stdlib.h>
#include <stdio.h>
#include <mem.h>
#include <lauxlib.h>
#include <lualib.h>
/* Maximální povolené rozměry bitmapy */
#define MAX_BITMAP_WIDTH 1024
#define MAX_BITMAP_HEIGHT 1024
/* Povolení kontroly rozsahu barvových komponent pixelu */
/*#define CHECK_COLOR_COMPONENTS*/
/* Makro, které na zásobník uloží zprávu o chybě */
#define LUA_ERROR(errorMessage) {lua_pushstring(L, (errorMessage)); lua_error(L);}
/* Makro pro kontrolu počtu parametrů */
#define NUMBER_OF_PARAMETERS(cnt) if (lua_gettop(L)!=(cnt)) LUA_ERROR("incorrect number of parameters")
/* Makro pro kontrolu, zda je parametr specifikovaný indexem, typu číslo */
#define NUMBERP(index) if (!lua_isnumber(L, (index))) LUA_ERROR("type mishmash - number expected")
/* Makro pro kontrolu, zda je parametr specifikovaný indexem, typu řetězec */
#define STRINGP(index) if (!lua_isstring(L, (index))) LUA_ERROR("type mishmash - string expected")
#define CHECK_RANGE(x, min, max, errorMessage) if ((x)<(min) || (x)>(max)) LUA_ERROR(errorMessage)
#define LUA_FUNC static int
#define LUA_OK return 0;
/* Struktura popisující bitmapu */
typedef struct
{
unsigned int width;
unsigned int height;
unsigned char *array;
unsigned long size;
} Bitmap;
Bitmap *bmp = NULL;
/*
* Vytvoření bitmapy s 24bpp (truecolor)
*/
Bitmap * bitmapCreate(unsigned int width, unsigned int height)
{
Bitmap *bitmap=(Bitmap*)malloc(sizeof(Bitmap));
if (bitmap == NULL)
{
return NULL;
}
bitmap->width=width;
bitmap->height=height;
bitmap->size=3*width*height;
bitmap->array=(unsigned char*)malloc(bitmap->size*sizeof(unsigned char));
if (bitmap->array == NULL)
{
free(bitmap);
return NULL;
}
return bitmap;
}
/*
* Dealokace paměti s bitmapou
*/
void bitmapDestroy(Bitmap *bitmap)
{
if (!bitmap || !bitmap->array)
{
return;
}
free(bitmap->array);
free(bitmap);
}
/*
* Vymazání obsahu bitmapy
*/
void bitmapClear(Bitmap *bitmap)
{
if (!bitmap || !bitmap->array)
{
return;
}
memset(bitmap->array, 0x00, bitmap->size);
}
/*
* Vykreslení jednoho pixelu do bitmapy. Pixel je zadaný
* svými souřadnicemi a barvou v barvovém prostoru RGB.
*/
void bitmapPutPixel(Bitmap *bitmap, int x, int y, unsigned char r, unsigned char g, unsigned char b)
{
unsigned char *p;
if (!bitmap || !bitmap->array)
{
return;
}
if (x<0 || y<0 || x>=bitmap->width || y>=bitmap->height)
{
return;
}
p=bitmap->array+(3*(x+y*bitmap->width));
*p++=r;
*p++=g;
*p=b;
}
/*
* Uložení bitmapy do souboru typu PPM (Portable PixelMap)
*/
int bitmapSave(Bitmap *bitmap, const char *name)
{
FILE *fout = fopen(name, "wb");
if (!bitmap || !bitmap->array)
{
return 0;
}
if (fout != NULL)
{
int result = 1;
/* Kontrola zápisu hlavičky */
result &= (fprintf(fout, "P6\n"\
"# Created by Lua script\n"
"%d %d\n255\n", bitmap->width, bitmap->height) > 0);
/* Kontrola zápisu vlastní bitmapy */
result &= (fwrite(bitmap->array, bitmap->size, 1, fout) == 1);
/* Kontrola uzavření souboru */
result &= (fclose(fout) == 0);
return result;
}
return 0;
}
/*
* Funkce volaná ze skriptu
*/
LUA_FUNC createBitmap(lua_State* L)
{
int width, height;
if (bmp)
{
LUA_ERROR("bitmap is already created");
}
/* Kontrola počtu parametrů */
NUMBER_OF_PARAMETERS(2);
/* Kontrola typu parametrů */
NUMBERP(1);
NUMBERP(2);
width = lua_tointeger(L, 1);
height = lua_tointeger(L, 2);
/* Kontrola hodnot parametrů */
CHECK_RANGE(width, 0, MAX_BITMAP_WIDTH, "bitmap width is out of range");
CHECK_RANGE(height, 0, MAX_BITMAP_HEIGHT, "bitmap height is out of range");
/* Vše v pořádku - vytvoříme bitmapu */
bmp = bitmapCreate(width, height);
if (bmp == NULL)
{
LUA_ERROR("bitmapCreate failed");
}
LUA_OK
}
/*
* Funkce volaná ze skriptu
*/
LUA_FUNC clearBitmap(lua_State* L)
{
/* Kontrola počtu parametrů */
NUMBER_OF_PARAMETERS(0);
if (bmp == NULL)
{
LUA_ERROR("bitmap does not exist");
}
bitmapClear(bmp);
LUA_OK
}
/*
* Funkce volaná ze skriptu
*/
LUA_FUNC saveBitmap(lua_State* L)
{
if (bmp == NULL)
{
LUA_ERROR("bitmap does not exist");
}
/* Kontrola počtu parametrů */
NUMBER_OF_PARAMETERS(1);
/* Kontrola typu parametrů */
STRINGP(1);
if (!bitmapSave(bmp, lua_tostring(L, 1)))
LUA_ERROR("save bitmap to file failed");
LUA_OK
}
/*
* Funkce volaná ze skriptu
*/
LUA_FUNC putpixel(lua_State* L)
{
int i, x, y, r, g, b;
if (bmp == NULL)
{
LUA_ERROR("bitmap does not exist");
}
/* Kontrola počtu parametrů */
NUMBER_OF_PARAMETERS(5);
/* Kontrola typu parametrů - 5 číselných hodnot */
for (i=1; i<=5; i++)
{
NUMBERP(i);
}
/* Kontrola hodnot parametrů */
x = lua_tointeger(L, 1);
y = lua_tointeger(L, 2);
r = lua_tointeger(L, 3);
g = lua_tointeger(L, 4);
b = lua_tointeger(L, 5);
CHECK_RANGE(x, 0, bmp->width-1, "x coordinate is out of range");
CHECK_RANGE(y, 0, bmp->height-1, "y coordinate is out of range");
#if defined(CHECK_COLOR_COMPONENTS)
CHECK_RANGE(r, 0, 255, "red color component outside 0-255");
CHECK_RANGE(g, 0, 255, "green color component outside 0-255");
CHECK_RANGE(b, 0, 255, "blue color component outside 0-255");
#endif
bitmapPutPixel(bmp, x, y, (unsigned char)r, (unsigned char)g, (unsigned char)b);
LUA_OK
}
/*
* Registrace funkcí dostupných pro programy (skripty) napsané v Lua
*/
void registerLuaFunctions(lua_State* L)
{
lua_register(L, "createBitmap", createBitmap);
lua_register(L, "clearBitmap", clearBitmap);
lua_register(L, "saveBitmap", saveBitmap);
lua_register(L, "putpixel", putpixel);
}
/*
* Výpis obsahu zásobníku intepreteru
*/
void printLuaStack(lua_State* L)
{
int i, max;
max = lua_gettop(L);
fprintf(stderr, "Stack items:\n");
for (i = 1; i <= max; i++)
{
fprintf(stderr, "%d/%d\t%s\n", i, max, lua_tostring(L, i));
}
}
/*
* Hlavní funkce konzolové aplikace
*/
int main(int argc, char **argv)
{
int result;
lua_State* L = lua_open();
luaL_openlibs(L);
registerLuaFunctions(L);
result = luaL_dofile(L, argv[1]);
/* Zpracování chyby */
if (result != 0)
{
fprintf(stderr, "Error # %d\n", result);
}
printLuaStack(L);
lua_close(L);
bitmapDestroy(bmp);
return (result != 0);
}
/*
* finito
*/
2. Testovací skripty využívající rozhraní pro bitmapovou grafiku
Výše popsaná aplikace sice rozšiřuje interpretr jazyka Lua o pouhé čtyři funkce, ovšem pro mnoho úloh počítačové grafiky jsou tyto čtyři funkce dostačující – ve skutečnosti s nimi lze napsat prakticky jakýkoli algoritmus jehož výsledkem je rastrový obrázek, včetně 3D grafiky; jediným problémem je rychlost výpočtu. V této kapitole bude uvedeno několik jednoduchých a krátkých (velikost menší než 1 kB) testovacích skriptů, jejichž výsledkem je buď statický rastrový obrázek, nebo sekvence obrázků, kterou lze za pomoci externích nástrojů (ppmtogif, gifsicle aj.) spojit do animace.
2.1 Vykreslení jednoduchého obrazce založeného na aliasu
-- Vytvoreni jednoducheho obrazce zalozeneho na aliasu
width=256
height=256
createBitmap(width, height)
clearBitmap()
for y=0, height-1 do
for x=0, width-1 do
local r=x
local g=127+127*math.cos(((x-width/2)^2+(y-64)^2)/10)
local b=y
putpixel(x, y, r, g, b)
end
end
saveBitmap("lua8_1.ppm")
-- finito

2.2 Vykreslení Mandelbrotovy množiny
-- Vykresleni Mandelbrotovy mnoziny
width=320
height=240
maxiter=120
function mandelbrot(x, y, maxiter)
local zx, zy, cx, cy=0, 0, x, y
local iter
for iter=0, maxiter do
local zx2, zy2 = zx*zx, zy*zy
-- z=z^2+c
zx, zy = zx2-zy2+cx, 2*zx*zy+cy
-- test na bailout
if zx2+zy2>4 then
return iter
end
end
return 0
end
createBitmap(width, height)
clearBitmap()
for y=0, height-1 do
for x=0, width-1 do
local i=mandelbrot(x/(width/4)-2, y/(height/3)-1.5, maxiter)
putpixel(x, y, 20*i, 40*i, 60*i)
end
end
saveBitmap("lua8_2.ppm")
-- finito

2.3 Napodobení demo efektu „plasmy“
-- Vykresleni plasmy
width=256
height=256
createBitmap(width, height)
clearBitmap()
function putpixel2(x, y, c)
putpixel(x, y, c, c, c)
end
function plasma(x1, y1, x2, y2, c1, c2, c3, c4)
local xc, yc = (x1+x2)/2, (y1+y2)/2
-- podminka pro rekurzivni deleni
if x2-x1<1 then
return
end
-- 1---12--2
-- | | |
-- 13--cc--24
-- | | |
-- 3---34--4
-- barvy ve stredech stran ctverce
local c12, c13, c24, c34 = (c1+c2)/2, (c1+c3)/2, (c2+c4)/2, (c3+c4)/2
-- posun prostredniho bodu
local cc=(c12+c34)/2+math.random(x2-x1)*2-(x2-x1)
cc=math.min(cc, 255)
cc=math.max(cc, 0)
putpixel2(x1, y1, c1)
putpixel2(xc, y1, c12)
putpixel2(x1, yc, c13)
putpixel2(xc, yc, cc)
-- rekurzivni rozdeleni ctverce
plasma(x1, y1, xc, yc, c1, c12, c13, cc)
plasma(xc, y1, x2, yc, c12, c2, cc, c24)
plasma(x1, yc, xc, y2, c13, cc, c3, c34)
plasma(xc, yc, x2, y2, cc, c24, c34, c4)
end
math.randomseed(42)
plasma(0, 0, width-1, height-1, 127, 0, 127, 240)
saveBitmap("lua8_3.ppm")
-- finito

2.4 RGB spirála
-- Vytvoreni obrazce se spiralou
width=256
height=256
createBitmap(width, height)
clearBitmap()
for y=0, 255 do
for x=0, 255 do
local xc=x-width/2
local yc=y-height/2
local angle=math.atan2(xc, yc)
local magnitude=math.sqrt(xc^2 + yc^2)
local r=127+127*math.cos(20*angle)
local g=127+127*math.cos(10*angle+1/4*magnitude)
local b=127+127*math.cos(00*angle+1/6*magnitude)
putpixel(x, y, r, g, b)
end
end
saveBitmap("lua8_4.ppm")
-- finito

2.5 Animace – amplitudová modulace (AM)
-- Animace amplitudove modulace pri postupne
-- zmene frekvence druheho signalu
width=400
height=300
createBitmap(width, height)
-- amplituda a frekvence prvniho signalu
a1=70
f1=4
-- amplituda a menici se frekvence druheho signalu
a2=30
for f2=10, 50 do
clearBitmap()
for x=0, width-1, 0.1 do
wave1 = a1*math.cos(f1*x/(width/2))
y=height/3 + wave1
putpixel(x, y, 255, 0, 0)
wave2 = a2*math.cos(f2*x/(width/2))
y=height/3 + wave2
putpixel(x, y, 0, 0, 255)
-- vypocet AM
y=3*height/4 + wave1*wave2/50
putpixel(x, y, 255, 255, 255)
end
saveBitmap("lua8_5_"..f2..".ppm")
end
-- finito

3. Objektově orientované programování v jazyku Lua
V předchozích částech tohoto seriálu jsme si popsali velkou část důležitých vlastností programovacího jazyka Lua. V dnešní části a v části navazující si ukážeme, jakým způsobem lze používat metatabulky a metametody při psaní skriptů využívajících objektově orientovaný přístup. Již v pátém dílu jsme si řekli, že Lua sice nenabízí pro deklaraci tříd a objektů vlastní syntaxi, ale to neznamená, že by objektově orientované programování nebylo možné – objekty lze vytvářet buď na základě uzávěrů (closures) při jejichž použití jsou atributy i metody objektu „zabaleny“ právě v uzávěru (ostatně stejný princip je využitý i v některých funkcionálních jazycích), a/nebo lze využít druhého způsobu založeného na asociativních polích a již zmíněných metatabulkách a metametodách. Tvorba objektů je pak ze sémantického hlediska podobná technice používané v JavaScriptu, který byl inspirovaný jazykem Self a takzvaným prototypováním. Právě těmito technikami se budeme zabývat v následujících kapitolách.
4. Vlastnosti asociativních polí
Mezi podporované datové typy programovacího jazyka Lua patří i asociativní pole, někdy také nazývané hashmapa (hešmapa) či hešovací mapa. Jedná se, jak jsme si již řekli ve třetí části seriálu, o datovou strukturu, v níž jsou uloženy dvojice klíč–hodnota, přičemž klíčem může být hodnota libovolného datového typu kromě typu nil a hodnota může být zcela libovolná (může se jednat i o další asociativní pole, řetězec, funkci atd.). Právě možnost uložení funkce do asociativního pole je důležitá při konstrukci objektů. V případě, že klíče jsou představovány posloupností přirozených čísel, jsou asociativní pole ze sémantického hlediska rovnocenná klasicky chápaným indexovaným polím. Jazyk Lua dokonce umožňuje, aby se při vytváření (konstrukci) asociativního pole klíče vynechaly – v tomto případě překladač automaticky potřebné klíče (indexy) doplní tak, že první hodnotě přiřadí klíč 1, druhé hodnotě 2 atd. (dojde tedy k vytvoření pole, jehož indexy jsou přirozená čísla). Následují ukázky vytvoření (konstrukce) různých asociativních polí:
-- konstrukce asociativního pole se třemi položkami
poleA={klic1="hodnota1", klic2="hodnota2", klic3="hodnota3"}
-- při vynechání klíčů se automaticky doplní hodnoty 1, 2 a 3
poleB={"hodnota1", "hodnota2", "hodnota3"}
-- promíchání obou předešlých způsobů ("hodnota2" má přiřazený klíč 1 a "hodnota4" klíč 2)
poleC={klic1="hodnota1", "hodnota2", klic2="hodnota3", "hodnota4"}
-- přepis hodnot některých položek ("hodnota1" na "hodnotax") asociativního pole
-- ve chvíli, kdy tyto položky mají stejné klíče (každý klíč musí být jedinečný)
poleD={klic1="hodnota1", "hodnota2", klic2="hodnota3", "hodnota4", klic1="hodnotax"}
Po zkonstruování asociativního pole je možné přistupovat k jeho jednotlivým prvkům pomocí zápisu, který je prakticky stejný ve většině současných programovacích jazyků: identifikátor_pole[klíč]. Za identifikátor (jméno) asociativního pole se do hranatých závorek zapíše klíč, což může být libovolná hodnota (typicky číslo či řetězec), proměnná či výraz. V případě, že se v asociativním poli nachází prvek s daným klíčem, je hodnota tohoto prvku vrácena. Pokud prvek naopak nalezen není, vrátí se hodnota nil. Kromě tohoto způsobu zápisu nabízí Lua i alternativní způsob (syntaktický cukr, syntactic sugar), který se často používá v případech, kdy jsou asociativní pole použita ve funkci záznamu (record, struct) či objektu (zde je možné, jak uvidíme dále, použít i variantu s dvojtečkou namísto tečky, přičemž překladač automaticky doplní k deklarované či volané funkci jeden parametr nazvaný self). Tento způsob se zapisuje následovně: identifikátor_pole.klíč, tj. klíč zde není uveden v hranatých závorkách, ale za identifikátorem pole, od něhož je oddělen tečkou (pokud se jedná o řetězec, není uzavřen v uvozovkách). Příklad použití:
pole={klic1="hodnota1", "hodnota2", klic2="hodnota3", "hodnota4"}
print(pole["klic1"])
print(pole["klic2"])
print(pole["klic3"]) -- neexistující prvek, vypíše se "nil"
print(pole.klic1)
print(pole.klic2)
print(pole.klic3) -- neexistující prvek, vypíše se "nil"
5. Vytvoření objektu pomocí uzávěru
Objekty s atributy (datovými složkami) i metodami, které s těmito atributy pracují, je možné poměrně jednoduchým způsobem vytvořit pomocí uzávěru. Funkce, která uzávěr vytváří, se v tomto případě chová jako konstruktor, který objekt explicitně vytvoří (jedná se o lokální proměnnou funkce) a následně vrátí volajícímu programu. Nejjednodušším způsobem, jakým je možné objekt reprezentovat, je asociativní pole popsané v předchozí kapitole. Do tohoto pole se uloží jak všechny atributy objektu, tak i funkce (vystupující v roli metod). Tento – řekněme funkcionální – přístup je ukázán na následujícím demonstračním příkladu, ve kterém je pomocí funkce/konstruktoru Complex vytvořen objekt reprezentující komplexní číslo. Tato funkce vrací lokální asociativní pole nazvané příhodně self, ve kterém jsou vytvořeny atributy real a imag i funkce/metody print a add. Každé volání funkce Complex vrací nové asociativní pole, tj. nový objekt, což je chování, které od konstruktoru většinou požadujeme (pokud bychom naopak potřebovali vytvořit jedináčka, postačuje odstranit klíčové slovo local). Povšimněte si, že v tomto případě nedošlo ke skrytí atributů před okolním programem (privátní atributy resp. metody):
-- Vytvoreni objektu pomoci uzaveru
-- Konstruktor objektu
function Complex(paramReal, paramImag)
-- asociativni pole, ve kterem jsou ulozeny
-- jak atributy objektu, tak i jeho metody
local self={}
-- vytvoreni a inicializace atributu
self.real = paramReal
self.imag = paramImag
-- vytvoreni metody print
self.print = function()
print(self.real.."+i"..self.imag)
end
-- vytvoreni metody add
self.add = function(paramReal, paramImag)
self.real = self.real + paramReal
self.imag = self.imag + paramImag
end
-- navratovou hodnotou konstruktoru je uzaver
return self
end
-- vytvoreni dvojice objektu
c1 = Complex(1, 2)
c2 = Complex(3, 4)
-- tisk hodnot obou objektu
c1.print()
c2.print()
-- zmena atributu prvniho objektu
c1.add(10, 20)
-- tisk hodnot obou objektu
c1.print()
c2.print()
-- finito
6. Deklarace metod vně konstruktoru
V předchozím programu byl celý objekt (= uzávěr) zkonstruován přímo ve funkci Complex. V některých případech, zejména při vytváření objektů s větším množstvím metod, nemusí být tento způsob příliš přehledný. Je však možné použít i jiný způsob, při němž jsou použity externí funkce (uložené v asociativním poli nazvaném Complex), které jako svůj první parametr akceptují asociativní pole s atributy, s nimiž má funkce pracovat. Pole s atributy je vytvořeno funkcí/konstruktorem Complex.new. Jednou z nevýhod tohoto přístupu je nutnost explicitního uvádění parametru self při vytváření funkcí/metod (v následující kapitole si ukážeme, jak je možné – opět pomocí syntaktického cukru, nikoli nové jazykové konstrukce – zařídit implicitní předávání tohoto parametru. Taktéž způsob volání jednotlivých metod je poněkud neobvyklý, protože se používá zápis Complex.print(objekt) namísto obvyklejšího a kratšího objekt.print(). I tento zápis lze samozřejmě v jazyku Lua použít, je ovšem nutné manipulovat s metatabulkou objektu, což je technika, kterou si vysvětlíme příště.
-- Deklarace metod vne konstruktoru
-- Asociativni pole obsahujici metody
Complex={}
-- Konstruktor (ve skutecnosti jen vhodne
-- pojmenovana funkce)
function Complex.new(paramReal, paramImag)
local self={}
self.real = paramReal
self.imag = paramImag
return self
end
-- Metoda print s explicitnim predanim parametru self
function Complex.print(self)
print(self.real.."+i"..self.imag)
end
-- Metoda add s explicitnim predanim parametru self
function Complex.add(self, paramReal, paramImag)
self.real = self.real + paramReal
self.imag = self.imag + paramImag
end
-- vytvoreni dvojice objektu
c = Complex.new(1, 2)
c2 = Complex.new(3, 4)
-- tisk hodnot obou objektu
Complex.print(c)
Complex.print(c2)
-- zmena atributu prvniho objektu
Complex.add(c, 10, 20)
-- tisk hodnot obou objektu
Complex.print(c)
Complex.print(c2)
-- finito
7. Alternativní způsob zápisu metod
V následujícím příkladu je ukázáno, jakým způsobem je možné deklarovat metody, do nichž se parametr nazvaný self předává implicitně, tj. není v hlavičce metody použitý. V případě, že se místo zápisu function A.b použije zápis function A:b, tj. tečka se přepíše na dvojtečku, doplní překladač automaticky self jako první parametr funkce, tj. tento parametr sice není v hlavičce funkce uveden, ale lze s ním uvnitř funkce normálně pracovat. Jedná se o pouhý syntaktický cukr, který nijak chování funkce nemění – už jsme si ostatně uvedli, že i vlastní zápis A.b je taktéž syntaktickým cukrem ekvivalentním k A[„b“]. Při samotném volání funkcí/metod je však stále nutné použít zápisu Complex.print(objekt), protože prozatím neumíme manipulovat s metatabulkami a metametodami. Až si v následující části seriálu ukážeme i tuto techniku, uvidíme, že i samotné volání metod objektu je možné zjednodušit a zpřehlednit, nehledě na to, že nám tím otevírá cesta k tvorbě hierarchie objektů (tj. k dědičnosti).
-- Alternativni zpusob deklarace a volani metod
-- Asociativni pole obsahujici metody
Complex={}
-- Konstruktor (ve skutecnosti jen vhodne
-- pojmenovana funkce)
function Complex.new(paramReal, paramImag)
local self={}
self.real = paramReal
self.imag = paramImag
return self
end
-- Metoda print s implicitnim predanim parametru self
function Complex:print()
print(self.real.."+i"..self.imag)
end
-- Metoda add s implicitnim predanim parametru self
function Complex:add(paramReal, paramImag)
self.real = self.real + paramReal
self.imag = self.imag + paramImag
end
-- vytvoreni dvojice objektu
c = Complex.new(1, 2)
c2 = Complex.new(3, 4)
-- tisk hodnot obou objektu
Complex.print(c)
Complex.print(c2)
-- zmena atributu prvniho objektu
Complex.add(c, 10, 20)
-- tisk hodnot obou objektu
Complex.print(c)
Complex.print(c2)
-- finito
8. Odkazy na Internetu
- EN Wikipedia: Prototype Based Programming,
http://en.wikipedia.org/wiki/Prototype_based_programming - Programming in Lua (first edition)
http://www.lua.org/pil/index.html - Lua home page
http://www.lua.org/ - Lua: vestavitelný minimalista
/clanky/lua-vestavitelny-minimalista/ - Lua
http://www.linuxexpres.cz/praxe/lua - CZ Wikipedia: Lua
http://cs.wikipedia.org/wiki/Lua - EN Wikipedia: Lua (programming language)
http://en.wikipedia.org/wiki/Lua_(programming_language) - The Lua Programming Language
http://www.tiobe.com/index.php/paperinfo/tpci/Lua.html - Lua Programming Gems
http://www.lua.org/gems/ - LuaForge
http://luaforge.net/ - Forge project tree
http://luaforge.net/softwaremap/trove_list.php
9. Obsah další části seriálu
V následující části seriálu o programovacím jazyce Lua budou popsány další možnosti tohoto jazyka při psaní skriptů využívajících objektově orientovaný přístup. Ukážeme si například, jakým způsobem lze s využitím metatabulek a metametod vytvořit settery a gettery (tj. metody používané pro nastavování popř. čtení atributů objektů) tak, aby se mohly zapisovat pomocí operátoru přiřazení, podobně jako například v jazyku C#, a nikoli explicitním voláním metody. Taktéž si ukážeme, jak lze přetěžovat operátory i další způsob volání metod, který je syntakticky shodný s voláním metod v jazycích C++ či Java. Ve výše uvedeném příkladu by se tedy namísto volání Complex.print(c1) použila konstrukce c1:print(), ovšem pouze za předpokladu, že je vhodným způsobem předefinováno chování metametody __index.