Programovací jazyk Lua vestavěný do aplikací

14. 4. 2009
Doba čtení: 15 minut

Sdílet

V šesté části seriálu o jazyce Lua si ukážeme, jakým způsobem lze zabudovat překladač a interpret tohoto jazyka do vlastních aplikací. Také si popíšeme nejdůležitější funkce z aplikačního programového rozhraní, pomocí něhož mohou skripty napsané v Lua komunikovat s aplikací, do níž je Lua zabudována.

Obsah

1. Programovací jazyk Lua v aplikacích
2. Vestavění překladače a interpretu jazyka Lua do vlastních aplikací
3. Aplikační programové rozhraní interpretu
4. První demonstrační příklad – spuštění skriptu napsaného v jazyce Lua
5. Druhý demonstrační příklad – spuštění skriptu uloženého v externím souboru
6. Předávání parametrů a návrat hodnot z céčkové funkce
7. Třetí demonstrační příklad – zavolání céčkové funkce ze skriptu
8. Zdrojové kódy všech demonstračních příkladů i testovacích skriptů

1. Programovací jazyk Lua v aplikacích

Jeden z důvodů relativně velké oblíbenosti programovacího jazyka Lua mezi vývojáři spočívá v tom, že její překladač i interpret je velmi snadno vestavitelný do dalších aplikací, což znamená, že do prakticky libovolného programu je možné zabudovat buď plnohodnotný překladač tohoto jazyka, nebo pouze tu část, která se stará o běh přeloženého bajtkódu. V některých typech aplikací, například počítačových hrách, totiž nemusí být nutné překládat nové zdrojové kódy, ale pouze spouštět bajtkód přeložený přímo výrobcem hry; další aplikace naopak mohou těžit z toho, že jsou uživatelsky skriptovatelné (viz většina moderních „Office“, programy typu CAD, grafické a textové editory a mnoho dalších programů). Samozřejmě se nejedná o unikátní vlastnost, protože i mnoho interpretů dalších programovacích jazyků lze vestavět do jiných aplikací – v poslední době se stává populární především JavaScript vedle již zavedeného Pythonu (OpenOffice.org, GIMP), Scheme (opět GIMP), Lispu (AutoCAD, Emacs) či Visual Basicu (MS Office a další aplikace).

Ovšem v případě Lua je její vestavění do aplikace skutečně snadné – z pohledu programátora (především pokud programuje v céčku či C++), který ve své aplikaci potřebuje použít nějaký skriptovací jazyk, se jedná o pouhých několik programových řádků s následným slinkováním s objektovým kódem uloženým v archivu liblua.a, jak si ostatně ukážeme v navazujících kapitolách. Vložením celého překladače a interpretu jazyka Lua včetně jeho podpůrného běhového prostředí (základní funkce, garbage collector aj.) se zvětší velikost výsledného spustitelného souboru o cca 70 kB, což není nijak závratná hodnota, především při porovnání velikostí interpretů dalších programovacích jazyků. Lua se právě z tohoto důvodu dokonce používá i na mikrořadičích s poměrně malou operační pamětí a pamětí ROM (v jedné z aplikací využívajících Lua byl použit mikrořadič s 64 kB RAM a 256 kB EEPROM). V tomto případě se většinou využívá pouze ta část interpretu, která se stará o běh přeloženého bajtkódu, v některých situacích se také mění základní numerický datový typ na šestnáctibitové či třicetidvoubitové hodnoty namísto hodnot uložených ve formátu plovoucí tečky (viz soubor luaconf.h, především definice LUA_NUMBER, dopad této změny si ukážeme příště).

Vestavěný interpret jazyka Lua do jisté míry řeší taktéž otázku bezpečnosti skriptů, aby se zabránilo šíření makrovirů, které byly tak „populární“ mezi uživateli jednoho rozšířeného kancelářského balíku. Problém bezpečnosti je řešen především izolací běhového prostředí skriptů od ostatního systému. Pouze přímo programátor aplikace, která má obsahovat překladač a interpret Lua, může (explicitně zapsaným importem příslušné knihovny) skriptům povolit například možnost práce se soubory, spouštění dalších programů přes volání os.execute() apod. Bez importu těchto knihoven je skriptu povoleno se svým okolím komunikovat pouze pomocí volání zaregistrovaných funkcí. Pro předávání parametrů se navíc používá zvláštní zásobník, ne standardní rámec procesu (na něj se ukládá pouze jeden ukazatel), takže skripty ani nemají možnost manipulovat se zásobníkem procesu pod kterým běží (tím se eliminují útoky typu stack overflow). Interpret provádí i základní kontrolu korektnosti předaného bajtkódu.

2. Vestavění překladače a interpretu jazyka Lua do vlastních aplikací

Překladač a interpret jazyka Lua je možné do programů vytvářených v céčku či C++ vložit velmi snadno. Lua se instaluje buď překladem ze zdrojových kódů (k řízení překladu je samozřejmě určen soubor Makefile) nebo pomocí balíčkovacího nástroje příslušné Linuxové distribuce (v tomto případě je nutné nainstalovat i variantu nazvanou „Lua devel“). V obou případech získáme několik hlavičkových souborů, především lua.h, lualib.h a lauxlib.h a taktéž archiv nazvaný liblua.a, jenž obsahuje objektové soubory potřebné pro sestavení aplikace s překladačem a interpretem Lua (pokud se nepoužije překladač gcc, může být název archivu s objektovými soubory odlišný, některé překladače například pracují se soubory majícími koncovku .lib). V současné verzi Lua 5.x obsahuje archiv následující soubory (výpis je proveden příkazem ar t liblua.a):

lapi.o
lcode.o
ldebug.o
ldo.o
ldump.o
lfunc.o
lgc.o
llex.o
lmem.o
lobject.o
lopcodes.o
lparser.o
lstate.o
lstring.o
ltable.o
ltm.o
lundump.o
lvm.o
lzio.o
lauxlib.o
lbaselib.o
ldblib.o
liolib.o
lmathlib.o
loslib.o
ltablib.o
lstrlib.o
loadlib.o
linit.o 

Překlad programu, ve kterém má být Lua zabudována, je poměrně snadný – musíme pouze zadat cestu k hlavičkovým souborům i archivu s objektovými soubory. Pokud se používá překladač gcc (na platformě Microsoft Windows se může jednat například o vývojové prostředí Mingw), provede se překlad následujícím způsobem:

gcc -ansi -Wall priklad.c -Ipath_to_lua/src path_to_lua/src/liblua.a 

První dvě volby nemusí být uvedeny, pouze si pomocí nich demonstrujeme, že hlavičkové soubory dodávané s Lua plně odpovídají standardům céčka (a současně také C++). V případě, že jsou hlavičkové soubory lua.h, lualib.h a lauxlib.h umístěny ve standardním adresáři prohledávaném céčkovým preprocesorem a archiv liblua.a je umístěn ve standardním adresáři prohledávaném linkerem, lze předchozí příkaz zjednodušit:

gcc -ansi -Wall priklad.c liblua.a 

Poznámka: odkazy na zdrojové kódy všech dále uvedených demonstračních příkladů, testovacích skriptů i výsledků běhu těchto skriptů jsou uvedeny v osmé kapitole.

3. Aplikační programové rozhraní interpretu

V první kapitole jsme si řekli, že aplikační programové rozhraní (API) interpretu Lua je jednoduché a současně i snadno použitelné. Skutečně tomu tak je. Celé rozhraní je dostupné přes několik konstant, datových typů (včetně struktur) a funkcí, jejichž hlavičky jsou umístěné v souborech lua.h, lualib.h (základ) a lauxlib.h (rozšíření a současně i zjednodušení rozhraní). Stav interpretu se ukládá do datové struktury nazvané lua_State, kterou je nutné před zavoláním skriptu vytvořit a inicializovat, například zavoláním lua_open() či nověji též luaL_newstate() nebo lua_newstate(). Prozatím nás nemusí zajímat, co je v této struktuře uloženo, ovšem v prakticky všech funkcích API se ukazatel na tuto strukturu předává (podobně se při práci se soubory používá struktura FILE, i když k jejím složkám se málokdy přistupuje přímo). V následující tabulce jsou vypsány některé základní funkce, se kterými se dále seznámíme v demonstračních příkladech:

Funkce Hlavička uložena Popis
lua_open lua.h vytvoření nové struktury lua_State
lua_close lua.h odstranění všech objektů asociovaných se stavem
lua_register lua.h registrace céčkové funkce, kterou bude možné volat ze skriptů
luaopen_base lualib.h načtení základní knihovny (obsahuje i funkci print)
luaL_dostring lauxlib.h spuštění skriptu uloženého v céčkovém řetězci
luaL_dofile lauxlib.h spuštění skriptu uloženého v externím souboru

4. První demonstrační příklad – spuštění skriptu napsaného v jazyce Lua

V dnešním prvním demonstračním příkladu je ukázán způsob spuštění skriptu napsaného v programovacím jazyce Lua, který je uložen v céčkovém řetězcovém literálu, tj. řetězcové konstantě umístěné přímo ve výsledném spustitelném souboru v kódovém segmentu (při prohlížení spustitelného souboru binárním editorem lze zdrojový kód skriptu poměrně rychle nalézt). Celý skript je spuštěn pomocí funkce luaL_dostring(), přičemž první parametr představuje objekt s uloženým stavem interpretu a druhý parametr je řetězec se skriptem (přesněji odkaz na paměť s uloženým řetězcem zakončeným nulou, jak je v céčku zvykem). Nejedná se sice o typický způsob ukládání skriptů, už jen kvůli nepřehlednosti zápisu řetězce na mnoha řádcích a problémy s některými znaky se speciálním významem (ty je zapotřebí převést na céčkovou znakovou entitu), v některých případech se však může hodit – například tehdy, když se má celá aplikace spouštět na mikrořadičích bez souborového systému či v případě požadavku, aby byl celý program i se všemi potřebnými daty uložen v jediném souboru, který tak není nutné instalovat, ale lze ho spouštět například přímo ze sítě či přenosného USB disku. Následuje výpis zdrojového kódu prvního demonstračního příkladu:

/*
 * Prvni demonstracni priklad - spusteni skriptu napsaneho
 * v programovacim jazyce Lua, ktery je ulozeny v Ceckovem
 * retezcovem literalu.
 */

#include <stdio.h>
#include <stdlib.h>

/* Zakladni a doplnkove funkce interpretu jazyka Lua */
#include <lualib.h>
#include <lauxlib.h>

/* Skript napsany v programovacim jazyce Lua */
const char * SCRIPT =
"-- Demonstracni priklad: pouziti uzaveru\n"\
"\n"\
"-- pomocna funkce vracejici uzaver\n"\
"function defPosloupnosti(n)\n"\
    "-- pamatovana hodnota, ktera vsak\n"\
    "-- neni z okolniho programu dostupna\n"\
    "local y = 1\n"\
    "-- pocitadlo volani = exponent\n"\
    "local index = 0\n"\
    "-- anonymni funkce vytiskne pamatovanou\n"\
    "-- hodnotu a nakonec ji vynasobi zvolenou konstantou\n"\
    "return function()\n"\
        "print(index, y)\n"\
        "y = y * n\n"\
        "index = index + 1\n"\
    "end\n"\
"end\n"\
"\n"\
"print('mocniny cisla 2')\n"\
"-- ziskani uzaveru\n"\
"generator = defPosloupnosti(2)\n"\
"\n"\
"-- postupne se budou tisknout\n"\
"-- mocniny cisla 2\n"\
"for i=0, 16 do\n"\
    "generator()\n"\
"end\n"\
"\n"\
"print()\n"\
"\n"\
"print('mocniny cisla 3')\n"\
"-- ziskani uzaveru\n"\
"generator = defPosloupnosti(3)\n"\
"\n"\
"-- postupne se budou tisknout\n"\
"-- mocniny cisla 3\n"\
"for i=0, 16 do\n"\
    "generator()\n"\
"end\n"\
"\n"\
"print()\n"\
"\n"\
"print('mocniny cisla 10')\n"\
"-- ziskani uzaveru\n"\
"generator = defPosloupnosti(10)\n"\
"\n"\
"-- postupne se budou tisknout\n"\
"-- mocniny cisla 3\n"\
"for i=0, 16 do\n"\
    "generator()\n"\
"end\n";

/* Hlavni funkce konzolove aplikace */
int main(void)
{
    int result;

    /* vytisteni hlavicky */
    puts(LUA_RELEASE);
    puts(LUA_COPYRIGHT);
    puts(LUA_AUTHORS);
    putchar('\n');

    /* vytvoreni objektu, do nejz se uklada stav interpretu */
    lua_State* L = lua_open();
    /* nacteme zakladni knihovnu obsahujici mj. i funkci print() */
    luaopen_base(L);
    /* nacteni retezce interpretem, jeho preklad a nasledne spusteni */
    result = luaL_dostring(L, SCRIPT);
    /* odstraneni vsech objektu asociovanych se stavem "Lua" */
    lua_close(L);
    if (result != 0)
    {
        printf("Error # %d\n", result);
    }
    /* vypocet navratoveho kodu */
    return (result != 0);
}

/* finito */ 

Po překladu a spuštění prvního demonstračního příkladu se na standardní výstup vypíše následující text:

Lua 5.1.3
Copyright (C) 1994-2008 Lua.org, PUC-Rio
R. Ierusalimschy, L. H. de Figueiredo & W. Celes

mocniny cisla 2
0       1
1       2
2       4
3       8
4       16
5       32
6       64
7       128
8       256
9       512
10      1024
11      2048
12      4096
13      8192
14      16384
15      32768
16      65536

mocniny cisla 3
0       1
1       3
2       9
3       27
4       81
5       243
6       729
7       2187
8       6561
9       19683
10      59049
11      177147
12      531441
13      1594323
14      4782969
15      14348907
16      43046721

mocniny cisla 10
0       1
1       10
2       100
3       1000
4       10000
5       100000
6       1000000
7       10000000
8       100000000
9       1000000000
10      10000000000
11      100000000000
12      1000000000000
13      10000000000000
14      1e+014
15      1e+015
16      1e+016 

5. Druhý demonstrační příklad – spuštění skriptu uloženého v externím souboru

Ve druhém demonstračním příkladu je ukázán způsob spuštění skriptu, jehož kód je uložen v externím souboru (a ne v řetězci, jak tomu bylo v příkladu předchozím). Externě uložený skript je buď možné programově načíst do céčkového řetězce, ovšem to je poměrně komplikované (musí se například předem zjišťovat délka řetězce, alokovat paměť pro řetězec atd.). Jednodušší je použít funkci luaL_dofile(), která načtení, překlad i spuštění skriptu provede automaticky. Návratovou hodnotou této funkce se signalizuje, zda skript proběhl korektně nebo zda v průběhu jeho načítání, překladu, spuštění či běhu došlo k nějaké chybě: v případě korektního běhu se vrátí nula, pokud nastane chyba, vrátí se jednička. Chybu je také možné vygenerovat programově, tj. ve skriptu, zavoláním funkce error(), popřípadě z céčkové funkce zavoláním lua_error() (řetězec obsahující chybové hlášení musí být v tomto případě uložen na zásobníku, což si ukážeme v následujících kapitolách). Zdrojový kód druhého demonstračního příkladu má následující tvar:

/*
 * Druhy demonstracni priklad - spusteni skriptu napsaneho
 * v programovacim jazyce Lua, ktery je ulozeny v externim
 * souboru.
 */

#include <stdio.h>
#include <stdlib.h>

/* Zakladni a doplnkove funkce interpretu jazyka Lua */
#include <lualib.h>
#include <lauxlib.h>

/* Hlavni funkce konzolove aplikace */
int main(int argc, char **argv)
{
    int result;
    if (argc != 2)
    {
        puts("Pouziti: lua62 script.lua");
        return 1;
    }

    /* vytisteni hlavicky */
    puts(LUA_RELEASE);
    puts(LUA_COPYRIGHT);
    puts(LUA_AUTHORS);
    putchar('\n');

    /* vytvoreni objektu, do nejz se uklada stav interpretu */
    lua_State* L = lua_open();
    /* nacteme zakladni knihovnu obsahujici mj. i funkci print() */
    luaopen_base(L);
    /* nacteni externiho skriptu, jeho preklad a nasledne spusteni */
    result = luaL_dofile(L, argv[1]);
    /* odstraneni vsech objektu asociovanych se stavem "Lua" */
    lua_close(L);
    if (result != 0)
    {
        printf("Error # %d\n", result);
    }
    /* vypocet navratoveho kodu */
    return (result != 0);
}

/* finito */ 

Po překladu druhého demonstračního příkladu lze výsledný spustitelný soubor zavolat se jménem následujícího skriptu:

-- Testovaci skript pro druhy demonstracni priklad
-- pouziti uzaveru

-- pomocna funkce vracejici uzaver
function defPosloupnosti(n)
    -- pamatovana hodnota, ktera vsak
    -- neni z okolniho programu dostupna
    local y = 1
    -- pocitadlo volani = exponent
    local index = 0
    -- anonymni funkce vytiskne pamatovanou
    -- hodnotu a nakonec ji vynasobi zvolenou konstantou
    return function()
        print(index, y)
        y = y * n
        index = index + 1
    end
end

print("mocniny cisla 2")
-- ziskani uzaveru
generator = defPosloupnosti(2)

-- postupne se budou tisknout
-- mocniny cisla 2
for i=0, 16 do
    generator()
end

print()

print("mocniny cisla 3")
-- ziskani uzaveru
generator = defPosloupnosti(3)

-- postupne se budou tisknout
-- mocniny cisla 3
for i=0, 16 do
    generator()
end

print()

print("mocniny cisla 10")
-- ziskani uzaveru
generator = defPosloupnosti(10)

-- postupne se budou tisknout
-- mocniny cisla 3
for i=0, 16 do
    generator()
end

-- finito 

Výsledek po spuštění druhého demonstračního příkladu je shodný s příkladem prvním, proto ho zde již nebudu uvádět.

6. Předávání parametrů a návrat hodnot z céčkové funkce

Jednou z nejdůležitějších funkcí API je zajištění předávání parametrů do céčkové funkce a návratu vypočtených hodnot zpět do Lua skriptu. Vzhledem k tomu, že v jazyku Lua se typy parametrů odvozují z hodnot uložených do proměnných, není možné již v době překladu provést stoprocentní typovou kontrolu – tu je nutné ponechat až na dobu běhu (runtime). Pro usnadnění práce s parametry předávanými ze skriptu do céčkové (C++) aplikace je k dispozici několik funkcí, především lua_gettop() (počet předaných parametrů) a sada funkcí pro zjištění skutečného typu i-tého parametru: lua_isnumber(), lua_isstring(), lua_iscfunction() atd. (bližší popis bude uveden příště). Kromě toho existují ještě konverzní funkce typu lua_tonumber(), lua_tointeger() či lua_tolstring(), pomocí nichž lze parametry konvertovat. Pro návrat hodnot z céčkové funkce do skriptu je určený zásobník vytvořený interpretem, jelikož v Lua je možné, jak jsme se již dozvěděli v předchozích částech seriálu, vracet hodnot několik. Pro ukládání hodnot do tohoto zásobníku slouží funkce typu lua_pushstring() a lua_pushnumber(). Nesmíme také zapomenout na to, že céčková funkce musí vrátit (příkazem return) celkový počet parametrů uložených na zásobník.

7. Třetí demonstrační příklad – zavolání céčkové funkce ze skriptu

Ve třetím demonstračním příkladu si ukážeme, jakým způsobem je možné předávat parametry céčkové funkci, i to, jak lze kontrolovat počet i typ parametrů a způsob předávání návratových hodnot zpět do Lua skriptu. V příkladu je vytvořena funkce nazvaná gcd(), pomocí níž je možné vypočítat největší společný dělitel (greatest common divisor) dvou celých čísel Euklidovým algoritmem, přičemž první číslo by mělo být větší než druhé. Tato funkce je zaregistrována pro možnost jejího zavolání z Lua skriptu. Po svém zavolání si funkce nejprve zkontroluje počet a typ parametrů a pouze tehdy, když jsou funkci předány dva parametry typu číslo, je funkce provedena. Výsledkem výpočtu je dvojice hodnot – největší společný dělitel a počet iterací Euklidova algoritmu, které bylo zapotřebí provést pro nalezení výsledku. Obě hodnoty jsou uloženy na zásobník vytvořený interpretem jazyka Lua; návratová hodnota funkce gcd() pak udává počet uložených výsledků (tj. vlastně hloubku zaplněného zásobníku). Zdrojový kód třetího demonstračního příkladu má tvar:

/*
 * Treti demonstracni priklad - zavolani ceckove funkce
 * z Lua skriptu.
 */

#include <stdio.h>
#include <stdlib.h>

/* Zakladni a doplnkove funkce interpretu jazyka Lua */
#include <lualib.h>
#include <lauxlib.h>

/* Vypocet nejvetsiho spolecneho delitele */
static int gcd(lua_State* L)
{
    int x, y, iter = 1;
    /* zjistit pocet parametru pri volani funkce */
    if (lua_gettop(L) != 2)
    {
        lua_pushstring(L, "incorrect argument");
        lua_error(L);
    }
    /* kontrola typu obou parametru */
    if (!lua_isnumber(L, 1))
    {
        lua_pushstring(L, "incorrect first argument");
        lua_error(L);
    }
    if (!lua_isnumber(L, 2))
    {
        lua_pushstring(L, "incorrect second argument");
        lua_error(L);
    }

    /* nacist parametry */
    x = lua_tonumber(L, 1);
    y = lua_tonumber(L, 2);

    /* vypocet nejvetsiho spolecneho delitele */
    while (x % y > 0)
    {
        int pom = y;
        y = x % y;
        x = pom;
        iter ++;
    }

    /* prvni vysledek */
    lua_pushnumber(L, y);

    /* druhy vysledek */
    lua_pushnumber(L, iter);

    /* ulozit pocet vysledku na zasobniku */
    return 2;
}

/* Hlavni funkce konzolove aplikace */
int main(int argc, char **argv)
{
    int result;
    if (argc != 2)
    {
        puts("Pouziti: lua63 script.lua");
        return 1;
    }

    /* vytisteni hlavicky */
    puts(LUA_RELEASE);
    puts(LUA_COPYRIGHT);
    puts(LUA_AUTHORS);
    putchar('\n');

    /* vytvoreni objektu, do nejz se uklada stav interpretu */
    lua_State* L = lua_open();
    /* nacteme zakladni knihovnu obsahujici mj. i funkci print() */
    luaopen_base(L);

    /* registrace ceckove funkce gcd() pod jmenem "gcd" */
    lua_register(L, "gcd", gcd);

    /* nacteni externiho skriptu, jeho preklad a nasledne spusteni */
    result = luaL_dofile(L, argv[1]);

    /* odstraneni vsech objektu asociovanych se stavem "Lua" */
    lua_close(L);
    if (result != 0)
    {
        printf("Error # %d\n", result);
    }
    /* vypocet navratoveho kodu */
    return (result != 0);
}

/* finito */ 

Pro otestování korektnosti céčkové funkce gcd() implementované a zaregistrované ve třetím demonstračním příkladu můžeme použít následující skript napsaný v programovacím jazyce Lua, který vypíše největší společné dělitele číselných hodnot od 1 do 12 a současně i počet kroků nutných pro jejich nalezení:

-- Testovaci skript pro treti demonstracni priklad

print("i", "j", "gcd(i,j)", "#of iterations")

for i=1, 12 do
    for j=1, i do
        -- funkce gcd() vraci dvojici hodnot
        result, iter = gcd(i, j)
        print(i, j, result, iter)
    end
end

-- finito 

Všimněte si, že syntaxe pro přiřazení výsledků volané funkce více proměnným nezávisí na tom, zda se volá funkce naprogramovaná v jazyce Lua či funkce céčková. Po spuštění třetího demonstračního příkladu s názvem skriptu předaného jako jediný parametr příkazové řádky získáme tabulku největších společných dělitelů. V prvních dvou sloupcích jsou zobrazeny hodnoty, pro něž se největší společný dělitel počítá, ve třetím sloupci vypočtený výsledek a ve sloupci čtvrtém počet iterací nutných pro získání výsledku:

bitcoin školení listopad 24

Lua 5.1.3
Copyright (C) 1994-2008 Lua.org, PUC-Rio
R. Ierusalimschy, L. H. de Figueiredo & W. Celes

i           j           gcd(i,j)    #of iterations
1           1           1           1
2           1           1           1
2           2           2           1
3           1           1           1
3           2           1           2
3           3           3           1
4           1           1           1
4           2           2           1
4           3           1           2
4           4           4           1
5           1           1           1
5           2           1           2
5           3           1           3
5           4           1           2
5           5           5           1
6           1           1           1
6           2           2           1
6           3           3           1
6           4           2           2
6           5           1           2
6           6           6           1
7           1           1           1
7           2           1           2
7           3           1           2
7           4           1           3
7           5           1           3
7           6           1           2
7           7           7           1
8           1           1           1
8           2           2           1
8           3           1           3
8           4           4           1
8           5           1           4
8           6           2           2
8           7           1           2
8           8           8           1
9           1           1           1
9           2           1           2
9           3           3           1
9           4           1           2
9           5           1           3
9           6           3           2
9           7           1           3
9           8           1           2
9           9           9           1
10          1           1           1
10          2           2           1
10          3           1           2
10          4           2           2
10          5           5           1
10          6           2           3
10          7           1           3
10          8           2           2
10          9           1           2
10          10          10          1
11          1           1           1
11          2           1           2
11          3           1           3
11          4           1           3
11          5           1           2
11          6           1           3
11          7           1           4
11          8           1           4
11          9           1           3
11          10          1           2
11          11          11          1
12          1           1           1
12          2           2           1
12          3           3           1
12          4           4           1
12          5           1           3
12          6           6           1
12          7           1           4
12          8           4           2
12          9           3           2
12          10          2           2
12          11          1           2
12          12          12          1 

8. Zdrojové kódy všech demonstračních příkladů i testovacích skriptů

V následující tabulce jsou uloženy odkazy na zdrojové kódy všech tří demonstračních příkladů popsaných v předchozích kapitolách. Taktéž jsou zde uvedeny skripty použité spolu s druhým i se třetím demonstračním příkladem a výsledky běhu všech tří příkladů (jedná se o standardní výstup přesměrovaný do souboru po převodu tabulátorů na mezery).

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.