Obsah
1. Funkce nth a přístup k prvkům nekonečných sekvencí
2. Operace prováděné interně ve funkci nth
3. Chování funkce taken pro nekonečné sekvence
4. Chování funkce split pro nekonečné sekvence
5. Zazipování několika nekonečných sekvencí do nové sekvence
6. Kdy se tedy vyhodnocují prvky nekonečné sekvence získané funkcí zip?
7. Zjednodušení programů s využitím funkce each
8. Výběr prvků do nové sekvence funkcí vyššího řádu filter
9. Výběr prvků na základě aplikace predikátu match – výběr řetězců podle vzorku
10. Rozdělení prvků v sekvenci do dvou sekvencí funkcí partition
12. Opakování sekvence funkcí/generátorem cycle
14. Přidání indexů k prvkům s využitím funkce enumerate
15. Proložení prvků v sekvenci konstantními hodnotami
16. Funkce vyššího řádu foldl neboli reduce
17. Přehled funkcí deklarovaných v knihovně Lua Fun
18. Repositář s demonstračními příklady
19. Články o programovacím jazyce Lua i o technologii LuaJITu
1. Funkce nth a přístup k prvkům nekonečných sekvencí
Na úvodní článek o knihovně Lua Fun dnes navážeme a dokončíme popis této užitečné a prakticky použitelné knihovny, která do značné míry rozšiřuje možnosti programovacího jazyka Lua. Nejdříve se budeme věnovat nekonečným sekvencím, tj. sekvencím hodnot vytvářených nějakou funkcí (či přesněji řečeno generátorem). Dále si popíšeme implementaci „klasických“ operací, které nalezneme v prakticky všech funkcionálních programovacích jazycích. Jedná se o operace typu map, filter a reduce (tato funkce se někdy nazývá fold či přesněji foldl). I tyto operace knihovna Lua Fun pochopitelně plně podporuje.
Nejdříve si připomeňme existenci funkce nazvané nth. Tato funkce umožňuje, jak již ostatně její název naznačuje, přístup k n-tému prvku sekvence, přičemž v programovacím jazyku Lua indexujeme prvky od jedničky a nikoli od nuly (což má své nesporné výhody, ovšem způsobuje to problémy při přechodu z programovacího jazyka z céčkové větve). Zajímavé je, že funkce nth dokáže pracovat jak s konečnými sekvencemi (zde může mít konstantní složitost), tak i se sekvencemi nekonečnými, kde je její složitost O(n), protože je nutné k požadovanému prvku doiterovat. Ostatně je to patrné i ze způsobu implementace této funkce, který vypadá následovně:
local nth = function(n, gen_x, param_x, state_x) assert(n > 0, "invalid first argument to nth") -- An optimization for arrays and strings if gen_x == ipairs_gen then return param_x[n] elseif gen_x == string_gen then if n <= #param_x then return string.sub(param_x, n, n) else return nil end end for i=1,n-1,1 do state_x = gen_x(param_x, state_x) if state_x == nil then return nil end end return return_if_not_empty(gen_x(param_x, state_x)) end
Vzhledem k tomu, že nth umožňuje získat prvek s určitým indexem i z nekonečné sekvence, můžeme si toto chování ověřit na dnešním prvním demonstračním příkladu, v němž budeme přistupovat k prvkům sekvence nulových čísel. Tuto sekvenci lze vytvořit generátorem zeros, který již byl popsán minule:
-- načtení knihovny Lua Fun a současně import symbolů do globálního jmenného prostoru require "fun"() -- generátor nul print("zeros()") g = zeros() -- pokus o přístup k prvkům sekvence print("10th item:", nth(10, g)) print("1000th item:", nth(1000, g)) print("5th item:", nth(5, g)) -- generátor pseudonáhodných čísel print() print("rands(100, 200)") g = rands(100, 200) -- pokus o přístup k prvkům sekvence print("10th item:", nth(10, g)) print("1000th item:", nth(1000, g)) print("5th item:", nth(5, g)) -- generátor stejných řetězců print() print("xrepeat('*')") g = xrepeat("*") -- pokus o přístup k prvkům sekvence print("10th item:", nth(10, g)) print("1000th item:", nth(1000, g)) print("5th item:", nth(5, g)) -- generátor druhých mocnin přirozených čísel print() print("tabulate()") g = tabulate(function(x) return (x+1)*(x+1) end) -- pokus o přístup k prvkům sekvence print("10th item:", nth(10, g)) print("1000th item:", nth(1000, g)) print("5th item:", nth(5, g))
Výsledek by měl po spuštění tohoto demonstračního příkladu vypadat následovně:
zeros() 10th item: 0 1000th item: 0 5th item: 0 rands(100, 200) 10th item: 155 1000th item: 144 5th item: 151 xrepeat('*') 10th item: * 1000th item: * 5th item: * tabulate() 10th item: 100 1000th item: 1000000 5th item: 25
Chování pro neplatné indexy (včetně indexu nulového) můžeme snadno ověřit přímo v interaktivní smyčce REPL programovacího jazyka Lua:
> require "fun"() > g = zeros() > print(nth(0, g)) ./fun.lua:299: invalid first argument to nth stack traceback: [C]: in function 'assert' ./fun.lua:299: in function <./fun.lua:298> (...tail calls...) stdin:1: in main chunk [C]: in ? > print(nth(-1, g)) ./fun.lua:299: invalid first argument to nth stack traceback: [C]: in function 'assert' ./fun.lua:299: in function <./fun.lua:298> (...tail calls...) stdin:1: in main chunk [C]: in ?
2. Operace prováděné interně ve funkci nth
Zdrojový kód funkce nth byl uveden v úvodní kapitole. Pojďme si nyní otestovat, jak tato funkce pracuje s nekonečnou sekvencí vytvářenou s použitím vlastního generátoru. Původní zdrojový kód generátoru jsme již použili minule – jedná se o generátor druhých mocnin (kvadrátů) kladných čísel:
-- vlastní generátor function my_generator(x) return (x+1)*(x+1) end
Povšimněte si, že se jedná o čistou funkci, jejíž návratová hodnota závisí pouze na hodnotě vstupního parametru. Navíc tato funkce nemá vedlejší efekty (side effect). Ovšem jeden vedlejší efekt můžeme přidat – funkce může vypisovat svůj vstup. To sice není příliš praktické chování, ale pro ladění se může hodit:
-- vlastní generátor function my_generator(x) print("(iter)", x) return (x+1)*(x+1) end
Tuto funkci použijeme ve vlastním generátoru:
-- inicializovat vlastní generátor g = tabulate(my_generator)
A následně použijeme společně s funkcí nth pro získání n-tého prvku nekonečné sekvence:
-- načtení knihovny Lua Fun a současně import symbolů do globálního jmenného prostoru require "fun"() -- pomocná funkce pro tisk prvků konečné sekvence function printSequence(sequence) for _, value in iter(sequence) do print(value) end end -- vlastní generátor function my_generator(x) print("(iter)", x) return (x+1)*(x+1) end -- inicializovat vlastní generátor g = tabulate(my_generator) -- pokus o přístup k prvkům sekvence print() print("getting 9th item") g9 = nth(9, g) print() print("getting 10th item") g10 = nth(10, g) print() print("getting 5th item") g5 = nth(5, g) print() print("9th item:", g9) print("10th item:", g10) print("5th item:", g5) -- pokus o přístup k nultému prvku (který neexistuje) print() print("getting 0th item") g0 = nth(0, g)
Podívejme se nyní na chování funkce nth:
getting 9th item (iter) 0 (iter) 1 (iter) 2 (iter) 3 (iter) 4 (iter) 5 (iter) 6 (iter) 7 (iter) 8 getting 10th item (iter) 0 (iter) 1 (iter) 2 (iter) 3 (iter) 4 (iter) 5 (iter) 6 (iter) 7 (iter) 8 (iter) 9 getting 5th item (iter) 0 (iter) 1 (iter) 2 (iter) 3 (iter) 4 9th item: 81 10th item: 100 5th item: 25 getting 0th item lua: ./fun.lua:299: invalid first argument to nth stack traceback: [C]: in function 'assert' ./fun.lua:299: in function <./fun.lua:298> (...tail calls...) 19_nth_infinite_sequence_state.lua:56: in main chunk [C]: in ?
Vidíme, že v případě nekonečných sekvencí vytvářených generátorem funkce nth musí generátor restartovat od prvního prvku, takže se v tomto případě skutečně jedná o časově relativně náročnou operaci se složitostí O(n). Na to je zapotřebí dávat pozor v reálných programech.
3. Chování funkce taken pro nekonečné sekvence
Naprosto stejným postupem, tj. upraveným generátorem vypisujícím svůj vstupní parametr:
-- vlastní generátor function my_generator(x) print("(iter)", x) return (x+1)*(x+1) end
můžeme otestovat i chování další funkce nazvané taken. I tuto funkci jsme si popsali minule, takže si dnes pouze připomeňme, že taken umožňuje získat prvních n prvků z nějaké sekvence, ať již se jedná o sekvenci konečnou či nekonečnou. Podívejme se nyní na jednoduchý demonstrační příklad, který nám umožní otestovat základní chování této funkce:
-- načtení knihovny Lua Fun a současně import symbolů do globálního jmenného prostoru require "fun"() -- pomocná funkce pro tisk prvků konečné sekvence function printSequence(sequence) for _, value in iter(sequence) do print(value) end end -- vlastní generátor function my_generator(x) print("(iter)", x) return (x+1)*(x+1) end -- inicializovat vlastní generátor g = tabulate(my_generator) -- první podsekvence print() print("take_n(5, sequence)") print("----------------------") t5 = take_n(5, g) print("done\n") printSequence(t5) -- druhá podsekvence print() print("take_n(10, sequence)") print("----------------------") t10 = take_n(10, g) print("done\n") printSequence(t10) -- třetí podsekvence print() print("take_n(0, sequence)") print("----------------------") t0 = take_n(0, g) print("done\n") printSequence(t0)
Po spuštění tohoto demonstračního příkladu získáme zajímavé výsledky, které ukazují, že funkce taken interně vytváří novou (pod)sekvenci, ale nijak se nesnaží přistupovat k prvkům původní sekvence (zpráva „done“ je vypsána ještě před ladicími zprávami „(iter)“). To je velmi užitečné, protože obecně platí, že tato funkce má časovou složitost O(1), a to nezávisle na délce sekvence (ta může být i nekonečná). A v mnoha algoritmech lze taken použít, aniž by se posléze všechny prvky výsledné (pod)sekvence musely skutečně vyhodnocovat:
take_n(5, sequence) ---------------------- done (iter) 0 1 (iter) 1 4 (iter) 2 9 (iter) 3 16 (iter) 4 25 take_n(10, sequence) ---------------------- done (iter) 0 1 (iter) 1 4 (iter) 2 9 (iter) 3 16 (iter) 4 25 (iter) 5 36 (iter) 6 49 (iter) 7 64 (iter) 8 81 (iter) 9 100 take_n(0, sequence) ---------------------- done
4. Chování funkce split pro nekonečné sekvence
Další funkcí, která má podobné chování při zpracování nekonečných sekvencí, je funkce split, kterou jsme si taktéž popsali v úvodním článku. Připomeňme si, že tato funkce vrátí dvě podsekvence, přičemž první podsekvence odpovídá volání taken a druhá volání dropn. Chování si ověříme na generátoru, který na standardní výstup vypisuje generované hodnoty:
-- načtení knihovny Lua Fun a současně import symbolů do globálního jmenného prostoru require "fun"() -- pomocná funkce pro tisk několika prvků nekonečné sekvence function printSequence(sequence) for _, value in iter(sequence) do print(value) end end -- vlastní generátor function my_generator(x) print("(iter)", x) return (x+1)*(x+1) end -- inicializovat vlastní generátor g = tabulate(my_generator) -- první dvojice podsekvencí print() print("split(5, sequence)") print("----------------------") s1, s2 = split(5, g) print("done") printSequence(s1) -- druhá dvojice podsekvencí print() print("split(10, sequence)") print("----------------------") s1, s2 = split(10, g) print("done") printSequence(s1) -- třetí dvojice podsekvencí print() print("split(0, sequence)") print("----------------------") s1, s2 = split(0, g) print("done") printSequence(s1)
Výsledek ukazuje, v jakém okamžiku jsou vyhodnocovány prvky první sekvence i podsekvence. První část původní sekvence je vyhodnocena dvakrát:
split(5, sequence) ---------------------- (iter) 0 (iter) 1 (iter) 2 (iter) 3 (iter) 4 (iter) 0 done 1 (iter) 1 4 (iter) 2 9 (iter) 3 16 (iter) 4 25 split(10, sequence) ---------------------- (iter) 0 (iter) 1 (iter) 2 (iter) 3 (iter) 4 (iter) 5 (iter) 6 (iter) 7 (iter) 8 (iter) 9 (iter) 0 done 1 (iter) 1 4 (iter) 2 9 (iter) 3 16 (iter) 4 25 (iter) 5 36 (iter) 6 49 (iter) 7 64 (iter) 8 81 (iter) 9 100 split(0, sequence) ---------------------- done
5. Zazipování několika nekonečných sekvencí do nové sekvence
V předchozích kapitolách jsme mohli vidět, že některé funkce musí vyhodnocovat prvky v sekvenci, zatímco jiné tuto časově náročnou operaci neprovádí. Do druhé skupiny patří i funkce nazvaná velmi příznačně zip, o které jsme se zmínili minule. Víme již, že tato funkce „zazipuje“ několik sekvencí do sekvence jediné, přičemž první prvek nové sekvence je složen z prvních prvků sekvencí původních (jedná se o tabulku, resp. přesněji řečeno o n-tici) atd. Pokud této funkci předáme sekvence s konečným počtem prvků, bude výsledkem taková sekvence, jejíž délka bude odpovídat sekvenci nejkratší. Co se však stane v případě, kdy budou všechny vstupní sekvence nekonečné? V tomto případě bude výsledkem taktéž nekonečná sekvence, jejíž prvky nebudou funkcí zip vyhodnocovány. Ostatně se o tom můžeme snadno přesvědčit spuštěním následujícího demonstračního příkladu:
-- načtení knihovny Lua Fun a současně import symbolů do globálního jmenného prostoru require "fun"() -- pomocná funkce pro tisk několika prvků nekonečné sekvence function printZippedSequence(sequence, n) i = 0 for index, a, b, c in iter(sequence) do i = i + 1 if i > n then break end print(index, a, b, c) end end -- vlastní generátor function my_generator(x) return (x+1)*(x+1) end -- inicializovat vlastní generátor g1 = tabulate(my_generator) -- další generátor nekonečné sekvence g2 = xrepeat("*") -- třetí generátor nekonečné sekvence g3 = zeros() -- vytvoření zipu ze tří sekvencí z1 = zip(g1, g2, g3) print() print("Zipped sequence (first ten items from infinite sequence)") print("--------------------------------------------------------") printZippedSequence(z1, 10)
Výsledná sekvence nazvaná z1 bude nekonečná, takže z ní vypíšeme jen prvních deset prvků:
Zipped sequence (first ten items from infinite sequnce) ------------------------------------------------------- table: 0x55a58afd17d0 1 * 0 table: 0x55a58afd1900 4 * 0 table: 0x55a58afd1a30 9 * 0 table: 0x55a58afc1d20 16 * 0 table: 0x55a58afc1e50 25 * 0 table: 0x55a58afc1f80 36 * 0 table: 0x55a58afc20b0 49 * 0 table: 0x55a58afe5be0 64 * 0 table: 0x55a58afe5d10 81 * 0 table: 0x55a58afe5e40 100 * 0
6. Kdy se tedy vyhodnocují prvky nekonečné sekvence získané funkcí zip?
Nepatrnou úpravou generátoru – konkrétně přidáním ladicí funkce print – můžeme zjistit, ve kterém okamžiku se vlastně vyhodnocují prvky nekonečné sekvence, která byla získána funkcí zip. Upravený zdrojový kód demonstračního příkladu může vypadat následovně:
-- načtení knihovny Lua Fun a současně import symbolů do globálního jmenného prostoru require "fun"() -- pomocná funkce pro tisk několika prvků nekonečné sekvence function printZippedSequence(sequence, n) i = 0 for index, a, b, c in iter(sequence) do i = i + 1 if i > n then break end print(index, a, b, c) end end -- vlastní generátor function my_generator(x) print("(iter)", x) return (x+1)*(x+1) end -- inicializovat vlastní generátor g1 = tabulate(my_generator) -- další generátor nekonečné sekvence g2 = xrepeat("*") -- třetí generátor nekonečné sekvence g3 = zeros() -- vytvoření zipu ze tří sekvencí z1 = zip(g1, g2, g3) print() print("Zipped sequence (first ten items from infinite sequnce)") print("-------------------------------------------------------") printZippedSequence(z1, 10)
Po spuštění tohoto demonstračního příkladu je patrné, že samotné zavolání funkce zip nevede k vyhodnocení prvků sekvencí na vstupu. Teprve pokus o přístup k jednotlivým prvkům v iterační smyčce ve svém důsledku vede k volání generátoru:
Zipped sequence (first ten items from infinite sequnce) ------------------------------------------------------- (iter) 0 table: 0x556e569c8780 1 * 0 (iter) 1 table: 0x556e569c8950 4 * 0 (iter) 2 table: 0x556e569b8d20 9 * 0 (iter) 3 table: 0x556e569b8e80 16 * 0 (iter) 4 table: 0x556e569b8fe0 25 * 0 (iter) 5 table: 0x556e569b9110 36 * 0 (iter) 6 table: 0x556e569b9270 49 * 0 (iter) 7 table: 0x556e569b93d0 64 * 0 (iter) 8 table: 0x556e569dcbb0 81 * 0 (iter) 9 table: 0x556e569dcd10 100 * 0 (iter) 10
7. Zjednodušení programů s využitím funkce each
V předchozích demonstračních příkladech jsme používali klasickou smyčku for pro procházení všemi prvky sekvence. V případě „zazipované“ sekvence složené ze tří konečných sekvencí mohl kód vypadat následovně:
-- pomocná funkce pro tisk prvků konečné sekvence function printZippedSequence(sequence) for _, a, b, c in iter(sequence) do print(a, b, c) end end
Ovšem pravděpodobně vás napadlo, že se nejedná o příliš elegantní ani univerzální kód, protože (kromě dalších věcí) musíme dopředu vědět, kolik hodnot každý prvek sekvence obsahuje, nehledě na to, že opakovaný zápis programové smyčky začne být únavný. Ovšem i v tomto případě nám může pomoci knihovna Lua Fun, protože obsahuje funkci (vyššího řádu) nazvanou each. Té se předají dvě hodnoty: první hodnotou je nějaká funkce volaná pro každý prvek sekvence a druhou hodnotou je vlastní sekvence. Funkce each bude postupně iterovat přes prvky sekvence a pro každý volat první funkci. Ta by měla mít nějaký vedlejší efekt. Příkladem může být výpis všech prvků konečné sekvence tím, že pro každý její prvek bude zavolána funkce print:
each(print, sequence)
Použití této velmi užitečné funkce je patrné ze zdrojového kódu dalšího demonstračního příkladu:
-- načtení knihovny Lua Fun a současně import symbolů do globálního jmenného prostoru require "fun"() -- pomocná funkce pro tisk prvků konečné sekvence function printSequence(sequence) each(print, sequence) end -- vlastní generátor function my_generator(x) return (x+1)*(x+1) end -- inicializovat vlastní generátor g1 = tabulate(my_generator) -- další generátor nekonečné sekvence g2 = xrepeat("*") -- třetí generátor nekonečné sekvence g3 = zeros() -- vytvoření zipu ze tří sekvencí z1 = zip(g1, g2, g3) -- získání prvních deseti prvků z původně nekonečné sekvence t = take_n(10, z1) -- výstup na terminál print() print("Zipped sequence (first ten items from infinite sequnce)") print("-------------------------------------------------------") printSequence(t)
S výsledky, které odpovídají příkazům předchozím:
Zipped sequence (first ten items from infinite sequnce) ------------------------------------------------------- 1 * 0 4 * 0 9 * 0 16 * 0 25 * 0 36 * 0 49 * 0 64 * 0 81 * 0 100 * 0
8. Výběr prvků do nové sekvence funkcí vyššího řádu filter
Funkci zip zmíněnou v předchozích kapitolách pravděpodobně mnoho čtenářů již zná z dalších programovacích jazyků. Týká se to i funkce nazvané filter, která dnes již patří k základní nabídce funkcí i v mainstreamových programovacích jazycích (kde bývá zobecněna pro zpracování libovolných streamů či sekvencí). I tato funkce je funkcí vyššího řádu, a to z toho důvodu, že jako svůj vstup akceptuje jinou funkci. Zde se konkrétně musí jednat o predikát, tedy o funkci vracející pro každý svůj vstup pravdivostní hodnotu true nebo false, popř. nil. Na základě výsledné hodnoty predikátu se právě zpracovávaný prvek buď objeví ve výstupní sekvenci či nikoli – tato funkce tedy obecně vrací sekvenci, ale mnohdy s odlišným počtem prvků, než kolik jich má seznam vstupní (pokud není predikát splněn pro žádný prvek ze vstupní sekvence, bude výsledkem prázdná sekvence, naopak však může vzniknout i sekvence nekonečná).
Podívejme se nyní na použití této funkce při tvorbě sekvence obsahující sudá čísla a jiné sekvence, tentokrát s čísly lichými:
-- načtení knihovny Lua Fun a současně import symbolů do globálního jmenného prostoru require "fun"() -- pomocná funkce pro tisk prvků konečné sekvence function printSequence(sequence) each(print, sequence) end -- vlastní generátor function my_generator(x) return (x+1)*(x+1) end -- inicializovat vlastní generátor g = tabulate(my_generator) -- první podsekvence print() print("take_n(10, sequence)") print("----------------------") t10 = take_n(10, g) printSequence(t10) -- provést filtraci f1 = filter(function(x) return x % 2 == 0 end, g) print() print("even results") print("----------------------") t5 = take_n(5, f1) printSequence(t5) -- provést filtraci f2 = filter(function(x) return not(x % 2 == 0) end, g) print() print("odd results") print("----------------------") t5 = take_n(5, f2) printSequence(t5)
Po spuštění demonstrační příklad nejprve vypíše původní sekvenci, posléze výběr z této sekvence (jen sudá čísla) a následně další výběr (jen liché hodnoty):
take_n(10, sequence) ---------------------- 1 4 9 16 25 36 49 64 81 100 even results ---------------------- 4 16 36 64 100 odd results ---------------------- 1 9 25 49 81
9. Výběr prvků na základě aplikace predikátu match – výběr řetězců podle vzorku
Kromě výše zmíněné funkce vyššího řádu filter nabízí knihovna Lua Fun programátorům i funkci pojmenovanou grep. A skutečně – tato funkce připomíná chování příkazu grep, protože postupně prochází prvky sekvence (ty by měly obsahovat řetězec) a na základě porovnání hodnoty těchto řetězců se vzorkem se příslušný prvek vloží do nové sekvence či nikoli. Operace prováděné funkcí grep si můžeme ukázat na jednoduchém demonstračním příkladu, který bude zpracovávat sekvenci se jmény všech měsíců. Nejdříve se vypíšou všechny měsíce obsahující písmeno „n“, dále měsíce začínající řetězcem „Cer“, poté měsíce končící řetězcem „en“ a nakonec všechny měsíce, přesněji měsíce odpovídající vzorku „.“:
-- načtení knihovny Lua Fun a současně import symbolů do globálního jmenného prostoru require "fun"() -- sekvence months = { "Leden", "Unor", "Brezen", "Duben", "Kveten", "Cerven", "Cervenec", "Srpen", "Zari", "Rijen", "Listopad", "Prosinec" } -- pomocná funkce pro tisk prvků konečné sekvence function printSequence(sequence) each(print, sequence) end print("Mesice obsahujici znak 'n'") print("--------------------------") g1 = grep("n", months) printSequence(g1) print() print("Mesice zacinajici na 'Cer'") print("--------------------------") g2 = grep("^Cer", months) printSequence(g2) print() print("Mesice koncici na 'en'") print("--------------------------") g3 = grep("en$", months) printSequence(g3) print() print("Vsechny mesice") print("--------------------------") g4 = grep(".", months) printSequence(g4)
Výsledky získané po spuštění tohoto příkladu vypadají následovně:
Mesice obsahujici znak 'n' -------------------------- Leden Unor Brezen Duben Kveten Cerven Cervenec Srpen Rijen Prosinec Mesice zacinajici na 'Cer' -------------------------- Cerven Cervenec Mesice koncici na 'en' -------------------------- Leden Brezen Duben Kveten Cerven Srpen Rijen Vsechny mesice -------------------------- Leden Unor Brezen Duben Kveten Cerven Cervenec Srpen Zari Rijen Listopad Prosinec
10. Rozdělení prvků v sekvenci do dvou sekvencí funkcí partition
Funkce filter popsaná v předchozím textu vybírá ze vstupní sekvence určité prvky na základě vyhodnocení jejich hodnoty (vůči libovolné podmínce). Ovšem někdy se dostaneme do situace, kdy je nutné prvky rozdělit do dvou skupin – prvky odpovídající podmínce a prvky, které podmínce neodpovídají. Toho lze samozřejmě dosáhnout i funkcí filter za předpokladu, že ji zavoláme poprvé s nějakou podmínkou a podruhé s negací podmínky, ovšem v knihovně Lua Fun je dostupná i funkce, která obě operace provede současně. Tato funkce se jmenuje partition a používá se následovně (připomíná funkci split):
-- načtení knihovny Lua Fun a současně import symbolů do globálního jmenného prostoru require "fun"() -- pomocná funkce pro tisk prvků konečné sekvence function printSequence(sequence) each(print, sequence) end -- vlastní generátor function my_generator(x) return (x+1)*(x+1) end -- inicializovat vlastní generátor g = tabulate(my_generator) -- první podsekvence print() print("take_n(10, sequence)") print("----------------------") t10 = take_n(10, g) printSequence(t10) -- provést filtraci f1, f2 = partition(function(x) return x % 2 == 0 end, g) print() print("even results") print("----------------------") t5 = take_n(5, f1) printSequence(t5) -- (filtrace uz byla provedena) print() print("odd results") print("----------------------") t5 = take_n(5, f2) printSequence(t5)
V příkladu je vstupní sekvence celých čísel rozdělena na čísla sudá a čísla lichá, takže by výsledek po spuštění příkladu měl vypadat takto:
take_n(10, sequence) ---------------------- 1 4 9 16 25 36 49 64 81 100 even results ---------------------- 4 16 36 64 100 odd results ---------------------- 1 9 25 49 81
11. Funkce vyššího řádu map
Zatímco v běžných imperativních programovacích jazycích se seznamy zpracovávají prvek po prvku s využitím nějaké formy programové smyčky, v jazyku Lua se při použití knihovny Fun setkáme spíše s aplikací několika funkcí vyššího řádu, které jako svůj vstup akceptují sekvenci a nějakou funkci, která je postupně aplikována buď na prvky sekvence, nebo na prvek sekvence a akumulátor, jehož hodnota se postupně při zpracovávání jednotlivých prvků sekvence mění. Výsledkem bývá buď nová sekvence, nebo výsledná hodnota akumulátoru. Tyto funkce se většinou nazývají map, filter a reduce či foldl. Funkci filter již známe, takže se nyní podívejme na funkci map. Ta taktéž prochází prvky sekvence a aplikuje na ně nějakou funkci, podobně jako each, ovšem namísto využití vedlejšího efektu se na základě návratových hodnot funkce map vytváří nová sekvence prvků.
Následuje příklad použití založený na rozhodování, zda je prvek sudý či nikoli. Druhá část demonstračního příkladu vypočte hodnoty faktoriálu pro prvních deset přirozených čísel:
-- načtení knihovny Lua Fun a současně import symbolů do globálního jmenného prostoru require "fun"() -- pomocná funkce pro tisk prvků konečné sekvence function printSequence(sequence) each(print, sequence) end -- vlastní generátor function my_generator(x) return (x+1)*(x+1) end -- inicializovat vlastní generátor g = tabulate(my_generator) -- první podsekvence print() print("take_n(10, sequence)") print("----------------------") t10 = take_n(10, g) printSequence(t10) -- provést transformaci m1 = map(function(x) return x % 2 == 0 end, g) print() print("even results?") print("----------------------") t5 = take_n(5, m1) printSequence(t5) -- funkce pro výpočet faktoriálu function factorial(n) for i = 1, n-1 do n = n * i end return n end -- provést výpočet faktoriálu m2 = map(factorial, range(10)) print() print("factorials") print("----------------------") printSequence(m2)
Výsledky po spuštění:
take_n(10, sequence) ---------------------- 1 4 9 16 25 36 49 64 81 100 even results? ---------------------- false true false true false factorials ---------------------- 1 2 6 24 120 720 5040 40320 362880 3628800
12. Opakování sekvence funkcí/generátorem cycle
V některých situacích se setkáme s požadavkem na to, aby se prvky v sekvenci stále opakovaly dokola. Pochopitelně opět nemusíme pracně psát vlastní generátor nebo sofistikovanou programovou smyčku. Namísto toho můžeme použít funkci nazvanou cycle, která danou sekvenci (konečnou!) bude neustále dokola opakovat a vytvoří z ní tak sekvenci nekonečnou. V dalším demonstračním příkladu je ukázáno, jak se vytvoří sekvence sudých čísel v intervalu 2..10 a z ní následně sekvence, která se neustále opakuje:
-- načtení knihovny Lua Fun a současně import symbolů do globálního jmenného prostoru require "fun"() -- pomocná funkce pro tisk několika prvků nekonečné sekvence function printSequence(sequence, n) i = 0 for index, value in iter(sequence) do i = i + 1 if i > n then break end print(index, value) end end -- inicializovat generátor celých čísel g = range(2, 10, 2) print("original sequence") print("---------------------------") printSequence(g, 20) c = cycle(g) print() print("cycles of original sequence") print("---------------------------") printSequence(c, 20)
Výsledek tohoto příkladu nebude nijak překvapivý:
original sequence --------------------------- 2 2 4 4 6 6 8 8 10 10 cycles of original sequence --------------------------- 2 2 4 4 6 6 8 8 10 10 2 2 4 4 6 6 8 8 10 10 2 2 4 4 6 6 8 8 10 10 2 2 4 4 6 6 8 8 10 10
Další příklad používá funkce cycle a zip aby vznikla nová sekvence ukazující, která hodnota je sudá a která lichá:
-- načtení knihovny Lua Fun a současně import symbolů do globálního jmenného prostoru require "fun"() -- pomocná funkce pro tisk několika prvků zazipované konečné sekvence function printZippedSequence(sequence) for index, a, b in iter(sequence) do print(index, a, b) end end -- inicializovat generátor celých čísel g = range(1, 20, 1) -- opakující se sekvence dvou slov c = cycle({"liché", "sudé"}) -- vytvoření zipu z obou sekvencí z = zip(g, c) -- s tiskem výsledku printZippedSequence(z)
Výsledky:
table: 0x5648aa99dd20 1 liché table: 0x5648aa99de30 2 sudé table: 0x5648aa99df40 3 liché table: 0x5648aa99e050 4 sudé table: 0x5648aa99e160 5 liché table: 0x5648aa99e270 6 sudé table: 0x5648aa97ccb0 7 liché table: 0x5648aa97cdc0 8 sudé table: 0x5648aa97ced0 9 liché table: 0x5648aa97cfe0 10 sudé table: 0x5648aa97d0f0 11 liché table: 0x5648aa97d200 12 sudé table: 0x5648aa97d310 13 liché table: 0x5648aa97d420 14 sudé table: 0x5648aa9a0a30 15 liché table: 0x5648aa9a0b40 16 sudé table: 0x5648aa9a0c50 17 liché table: 0x5648aa9a0d60 18 sudé table: 0x5648aa9a0e70 19 liché table: 0x5648aa9a0f80 20 sudé
A konečně příklad třetí přidává informaci o tom, které celé číslo je dělitelné třemi, opět na základě použití funkcí range, cycle a zip (bez použití programové smyčky):
-- načtení knihovny Lua Fun a současně import symbolů do globálního jmenného prostoru require "fun"() -- pomocná funkce pro tisk několika prvků zazipované konečné sekvence function printZippedSequence(sequence) for index, a, b, c in iter(sequence) do print(index, a, b, c) end end -- inicializovat generátor celých čísel g = range(1, 20, 1) -- opakující se sekvence dvou slov c1 = cycle({"liché", "sudé"}) -- opakující se sekvence tří slov c2 = cycle({"", "", "a dělitelné třemi"}) -- vytvoření zipu z obou sekvencí z = zip(g, c1, c2) -- s tiskem výsledku printZippedSequence(z)
Výsledky:
table: 0x558981730950 1 liché table: 0x558981720cc0 2 sudé table: 0x558981720df0 3 liché a dělitelné třemi table: 0x558981720f70 4 sudé table: 0x5589817210a0 5 liché table: 0x558981745030 6 sudé a dělitelné třemi table: 0x558981745160 7 liché table: 0x558981745290 8 sudé table: 0x5589817453c0 9 liché a dělitelné třemi table: 0x5589817497a0 10 sudé table: 0x5589817498d0 11 liché table: 0x558981749a00 12 sudé a dělitelné třemi table: 0x558981749b30 13 liché table: 0x558981749c60 14 sudé table: 0x558981725940 15 liché a dělitelné třemi table: 0x558981725a70 16 sudé table: 0x558981725ba0 17 liché table: 0x558981725cd0 18 sudé a dělitelné třemi table: 0x558981725e00 19 liché table: 0x558981725f30 20 sudé
13. Kompozice sekvencí
Již víme, jak z několika sekvencí udělat sekvenci jedinou jejich „zazipováním“. Existuje však ještě jeden způsob, jak sekvence nějakým způsobem zkombinovat. Jedná se o operaci pojmenovanou chain, která vytvoří novou sekvenci spojením všech vstupních sekvencí do výsledné řady. Aby byla tato funkce prakticky použitelná je nutné, aby byly všechny sekvence konečné, popř. aby byla nekonečná jen poslední vstupní sekvence. Opět se podívejme na jednoduchý příklad použití:
-- načtení knihovny Lua Fun a současně import symbolů do globálního jmenného prostoru require "fun"() -- pomocná funkce pro tisk několika prvků konečné sekvence function printSequence(sequence) for index, value in iter(sequence) do print(index, value) end end -- inicializovat generátor celých čísel g1 = range(2, 10, 2) -- další sekvence g2 = range(10, 2, -2) -- třetí sekvence g3 = xrepeat("***") -- spojení všech tří sekvencí chained = chain(g1, g2, g3) -- získání prvních dvaceti prvků z výsledné nekonečné sekvence trimmed = take_n(20, chained) print() print("chained and trimmed sequence") print("----------------------------") printSequence(trimmed) -- vše v jediném příkazu trimmed2 = take_n(20, chain(range(2, 10, 2), range(10, 2, -2), xrepeat(100))) print() print("chained and trimmed sequence") print("----------------------------") printSequence(trimmed)
První dvě vstupní sekvence jsou konečné, jen třetí je nekonečná, takže spojení všech tří sekvencí dopadne podle očekávání:
chained and trimmed sequence ---------------------------- table: 0x559dbad78a60 2 table: 0x559dbad78c40 4 table: 0x559dbad68d60 6 table: 0x559dbad68ee0 8 table: 0x559dbad69060 10 table: 0x559dbad69250 10 table: 0x559dbad693a0 8 table: 0x559dbad91020 6 table: 0x559dbad91170 4 table: 0x559dbad912c0 2 table: 0x559dbad91480 *** table: 0x559dbad915d0 *** table: 0x559dbad91720 *** table: 0x559dbad91870 *** table: 0x559dbad919c0 *** table: 0x559dbad91b10 *** table: 0x559dbad91c60 *** table: 0x559dbad6e7b0 *** table: 0x559dbad6e900 *** table: 0x559dbad6ea50 *** chained and trimmed sequence ---------------------------- table: 0x559dbad6f680 2 table: 0x559dbad93df0 4 table: 0x559dbad93f40 6 table: 0x559dbad94090 8 table: 0x559dbad941e0 10 table: 0x559dbad943a0 10 table: 0x559dbad944f0 8 table: 0x559dbad94640 6 table: 0x559dbad94790 4 table: 0x559dbad948e0 2 table: 0x559dbad94aa0 *** table: 0x559dbad94bf0 *** table: 0x559dbad94d40 *** table: 0x559dbad94e90 *** table: 0x559dbad94fe0 *** table: 0x559dbad95130 *** table: 0x559dbad95280 *** table: 0x559dbad953d0 *** table: 0x559dbad95520 *** table: 0x559dbad95670 ***
14. Přidání indexů k prvkům s využitím funkce enumerate
Funkce pojmenovaná enumerate může být užitečná v těch případech, kdy potřebujeme prvky v nějaké sekvenci očíslovat. Samozřejmě je možné enumerate nahradit nějakou kombinací funkcí range a zip, to je ovšem obtížnější v případě, že je vstupní sekvence nekonečná (funkce range se v knihovně Lua Fun chová jinak, než například v programovacím jazyce Clojure). Do kódu předchozího demonstračního příkladu jsme přidali funkci enumerate a rozšířili tak vstupní sekvenci o nový sloupec s pořadovým číslem prvku (opět se čísluje od jedničky, jak je to ostatně v jazyce Lua zvykem):
-- načtení knihovny Lua Fun a současně import symbolů do globálního jmenného prostoru require "fun"() -- pomocná funkce pro tisk několika prvků konečné sekvence function printSequence(sequence) for _, a, b in iter(sequence) do print(a, b) end end -- inicializovat generátor celých čísel g1 = range(2, 10, 2) -- další sekvence g2 = range(10, 2, -2) -- třetí sekvence g3 = xrepeat("***") -- spojení všech tří sekvencí chained = chain(g1, g2, g3) -- získání prvních dvaceti prvků z výsledné nekonečné sekvence trimmed = take_n(20, chained) enumerated = enumerate(trimmed) print() print("chained and trimmed sequence") print("----------------------------") printSequence(enumerated) -- vše v jediném příkazu trimmed2 = take_n(20, chain(range(2, 10, 2), range(10, 2, -2), xrepeat(100))) enumerated2 = enumerate(trimmed2) print() print("chained and trimmed sequence") print("----------------------------") printSequence(enumerated2)
Výsledky:
chained and trimmed sequence ---------------------------- 1 2 2 4 3 6 4 8 5 10 6 10 7 8 8 6 9 4 10 2 11 *** 12 *** 13 *** 14 *** 15 *** 16 *** 17 *** 18 *** 19 *** 20 *** chained and trimmed sequence ---------------------------- 1 2 2 4 3 6 4 8 5 10 6 10 7 8 8 6 9 4 10 2 11 100 12 100 13 100 14 100 15 100 16 100 17 100 18 100 19 100 20 100
15. Proložení prvků v sekvenci konstantními hodnotami
Předposlední funkcí, s níž se setkáme, je funkce nazvaná intersperse (popravdě jsem toto slovo předtím neznal). Tato funkce slouží k vytvoření nové sekvence ze sekvence vstupní. Ovšem v nové sekvenci budou prvky proloženy nějakou další hodnotou, což v praxi znamená, že u konečných sekvencí vznikne nová sekvence s dvojnásobnou délkou:
-- načtení knihovny Lua Fun a současně import symbolů do globálního jmenného prostoru require "fun"() -- pomocná funkce pro tisk několika prvků konečné sekvence function printSequence(sequence) for _, x in iter(sequence) do print(x) end end -- inicializovat generátor celých čísel g1 = range(1, 10, 1) g2 = intersperse("---------------------", g1) print() print("interspersed sequence") print("---------------------") printSequence(g2)
Výsledek:
interspersed sequence --------------------- 1 --------------------- 2 --------------------- 3 --------------------- 4 --------------------- 5 --------------------- 6 --------------------- 7 --------------------- 8 --------------------- 9 --------------------- 10 ---------------------
16. Funkce vyššího řádu foldl neboli reduce
Ve většině programovacích jazyků inspirovaných funkcionálním programováním se kromě funkcí typu map a filter setkáme i s funkcí vyššího řádu, která se nazývá buď reduce nebo fold. Knihovna Lua Fun pochopitelně není výjimkou, takže i v ní nalezneme tento typ funkce, a to dokonce s oběma jmény. Základní funkcí tohoto typu je funkce nazvaná foldl, která postupně zpracovává všechny prvky sekvence zleva doprava a aplikuje na každý prvek a akumulovanou hodnotu nějakou funkci (tedy foldl je funkcí vyššího řádu). Výsledkem je v každé iteraci nová hodnota akumulátoru a po projití celé vstupní sekvence je výsledná hodnota uložená v akumulátoru současně i návratovou hodnotou funkce foldl. Samotné volání této funkce je ovšem nepatrně složitější, než tomu bylo u map a filter. Je tomu tak z toho důvodu, že kromě zpracovávající funkce (se dvěma parametry) a vstupního seznamu musíme funkci foldl předat i počáteční hodnotu akumulátoru.
Typickým příkladem použití je součet aritmetické řady. Zde je akumulátor inicializován na nulovou hodnotu:
-- načtení knihovny Lua Fun a současně import symbolů do globálního jmenného prostoru require "fun"() -- inicializovat generátor celých čísel g1 = range(1, 10, 1) -- spočítat součet všech prvků v sekvenci sum = foldl(function(acc, x) return acc + x end, 0, g1) -- a vypsat ho print("1 + 2 + ... + 10 = ", sum)
S výsledkem:
1 + 2 + ... + 10 = 55
Dalším poměrně typickým příkladem použití je výpočet faktoriálu a to tak, že nejdříve vytvoříme aritmetickou řadu od 1 do n a posléze prvky této řady vynásobíme. Akumulátor musí být v tomto případě inicializován na jedničku (protože násobení nulou pochopitelně nedává žádané výsledky):
-- načtení knihovny Lua Fun a současně import symbolů do globálního jmenného prostoru require "fun"() for n = 1, 20 do -- inicializovat generátor celých čísel g1 = range(1, n) prod = foldl(function(acc, x) return acc * x end, 1, g1) print(n.."! = ", prod) end
Výsledky:
1! = 1 2! = 2 3! = 6 4! = 24 5! = 120 6! = 720 7! = 5040 8! = 40320 9! = 362880 10! = 3628800 11! = 39916800 12! = 479001600 13! = 6227020800 14! = 87178291200 15! = 1307674368000 16! = 20922789888000 17! = 355687428096000 18! = 6402373705728000 19! = 121645100408832000 20! = 2432902008176640000
Protože se s výpočtem sumy nebo produktu (násobení prvků řady) setkáme velmi často, existují v knihovně Lua Fun pomocné funkce sum a prod, které nahrazují volání foldl:
-- načtení knihovny Lua Fun a současně import symbolů do globálního jmenného prostoru require "fun"() for n = 1, 20 do -- inicializovat generátor celých čísel g1 = range(1, n) prod = product(g1) print(n.."! = ", prod) end -- výpočet na jediném řádku print() for n = 1, 20 do -- inicializovat generátor celých čísel print(n.."! = ", product(range(1, n))) end
Výsledky:
1! = 1 2! = 2 3! = 6 4! = 24 5! = 120 6! = 720 7! = 5040 8! = 40320 9! = 362880 10! = 3628800 11! = 39916800 12! = 479001600 13! = 6227020800 14! = 87178291200 15! = 1307674368000 16! = 20922789888000 17! = 355687428096000 18! = 6402373705728000 19! = 121645100408832000 20! = 2432902008176640000 1! = 1 2! = 2 3! = 6 4! = 24 5! = 120 6! = 720 7! = 5040 8! = 40320 9! = 362880 10! = 3628800 11! = 39916800 12! = 479001600 13! = 6227020800 14! = 87178291200 15! = 1307674368000 16! = 20922789888000 17! = 355687428096000 18! = 6402373705728000 19! = 121645100408832000 20! = 2432902008176640000
17. Přehled funkcí deklarovaných v knihovně Lua Fun
V této kapitole bude uveden přehled většiny funkcí deklarovaných v knihovně Lua Fun. Funkce jsou rozděleny podle toho, jakou operaci provádí a pro jaké účely se hodí.
Iterátory, resp. funkce nahrazující klasické iterační smyčky:
# | Funkce | Stručný popis |
---|---|---|
1 | iter | vytvoření iterátoru z různých typů jazyka Lua (pole, mapa, řetězec) |
2 | each | spuštění zvolené funkce pro každý prvek iterátoru |
3 | for_each | jmenný alias pro předchozí funkci |
4 | foreach | jmenný alias pro předchozí funkci |
Generátory:
# | Funkce | Stručný popis |
---|---|---|
1 | range | série číselných hodnot mezi zadanými mezemi s určitým krokem mezi hodnotami |
2 | duplicate | sekvence jedné opakující se hodnoty |
3 | xrepeat | alias pro předchozí generátor |
4 | replicate | alias pro předchozí generátor |
5 | tabulate | sekvence generovaná nějakou funkcí nebo uzávěrem |
6 | zeros | série nulových hodnot |
7 | ones | série jedniček |
8 | rands | série pseudonáhodných hodnot |
Přístup k prvkům, řezy sekvencí (slicing) atd.:
# | Funkce | Stručný popis |
---|---|---|
1 | nth | získání n-tého prvku sekvence |
2 | head | získání prvního prvku v sekvenci |
3 | car | jmenný alias pro předchozí funkci |
4 | tail | sekvence bez prvního prvku |
5 | cdr | jmenný alias pro předchozí funkci |
6 | taken | získání prvních n prvků ze sekvence |
7 | take_while | získání prvků ze začátku sekvence na základě zadané podmínky |
8 | take | jmenný alias pro předchozí dvě funkce (v závislosti na typu prvního parametru) |
9 | dropn | přeskočení prvních n prvků ze sekvence |
10 | drop_while | získání nové sekvence získané přeskočením prvků na začátku základě podmínky |
11 | drop | jmenný alias pro předchozí dvě funkce (v závislosti na typu prvního parametru) |
12 | span | rozdělení sekvence na dvě části – jako kombinace taken+dropn |
13 | split | jmenný alias pro předchozí funkci |
14 | split_at | jmenný alias pro předchozí funkci |
Filtrace (výběr) prvků na základě podmínky:
# | Funkce | Stručný popis |
---|---|---|
1 | filter | výběr prvků ze sekvence na základě kritérií testovaných predikátem |
2 | remove_if | jmenný alias pro předchozí funkci |
3 | grep | výběr prvků ze sekvence na základě kritérií testovaných vzorkem řetězce |
4 | partition | kombinace funkce filter + filter s opačnou podmínkou |
Iterace nad všemi prvky sekvence:
# | Funkce | Stručný popis |
---|---|---|
1 | map | aplikace nějaké funkce na všechny prvky sekvence |
2 | enumerate | vytvoření nové sekvence, v níž budou prvky očíslovány |
3 | intersperse | proložení prvků v sekvenci zvolenou konstantní hodnotou |
Kompozice sekvencí:
# | Funkce | Stručný popis |
---|---|---|
1 | zip | „zazipování“ dvou nebo většího množství sekvencí do sekvence výsledné |
2 | cycle | generátor sekvence, která neustále opakuje sekvenci vstupní |
3 | chain | spojení dvou nebo většího množství sekvencí do sekvence výsledné |
Operátory používané ve funkcích vyššího řádu max, min apod.:
# | Funkce | Stručný popis |
---|---|---|
1 | operator.le | porovnání na relaci menší nebo rovno |
2 | operator.lt | porovnání na relaci menší než |
3 | operator.eq | porovnání na relaci rovnosti |
4 | operator.ne | porovnání na relaci nerovnosti |
5 | operator.ge | porovnání na relaci větší nebo rovno |
6 | operator.gt | porovnání na relaci větší než |
7 | operator.add | součet |
8 | operator.sub | rozdíl |
9 | operator.mul | součin |
10 | operator.div | podíl (podle hodnot) |
11 | operator.truediv | dělení reálných čísel |
12 | operator.floordiv | dělení se zaokrouhlením |
13 | operator.intdiv | celočíselné dělení |
14 | operator.mod | zbytek po dělení (modulo) |
15 | operator.neq | změna znaménka |
16 | operator.pow | umocnění |
17 | operator.land | operace and |
18 | operator.lor | operace or |
19 | operator.lnot | operace not |
20 | operator.truth | operace not not (převod na true/false) |
21 | operator.concat | spojení řetězců |
22 | operator.len | délka tabulky/řetězce |
23 | operator.length | alias |
18. Repositář s demonstračními příklady
Zdrojové kódy všech dnes použitých demonstračních příkladů byly uloženy do nového Git repositáře, který je dostupný na adrese https://github.com/tisnik/functional-lua. V případě, že nebudete chtít klonovat celý repositář (ten je ovšem – alespoň prozatím – velmi malý, dnes má přibližně několik jednotek kilobajtů), můžete namísto toho použít odkazy na jednotlivé demonstrační příklady a jejich části, které naleznete v následující tabulce:
19. Články o programovacím jazyce Lua i o technologii LuaJITu
Programovacím jazykem Lua jsme se již na stránkách Rootu poměrně podrobně zabývali v samostatném seriálu. Jedná se o snadno naučitelný jazyk, který je ovšem (mj. i díky konceptu metatabulek) rozšiřitelný a poměrně tvárný. Viz též následující odkazy na již vydané články (včetně odkazu na e-book, jenž na základě těchto článků později vznikl):
- Programovací jazyk Lua
https://www.root.cz/clanky/programovaci-jazyk-lua/ - Základní konstrukce v programovacím jazyku Lua
https://www.root.cz/clanky/zakladni-konstrukce-v-programovacim-jazyku-lua/ - Operátory a asociativní pole v jazyku Lua
https://www.root.cz/clanky/operatory-a-asociativni-pole-v-jazyku-lua/ - Funkce v programovacím jazyku Lua
https://www.root.cz/clanky/funkce-v-programovacim-jazyku-lua/ - Funkce v programovacím jazyku Lua – uzávěry
https://www.root.cz/clanky/funkce-v-programovacim-jazyku-lua-uzavery/ - Programovací jazyk Lua vestavěný do aplikací
https://www.root.cz/clanky/programovaci-jazyk-lua-vestaveny-do-aplikaci/ - Programovací jazyk Lua v aplikacích II
https://www.root.cz/clanky/programovaci-jazyk-lua-v-aplikacich-ii/ - Objektově orientované programování v Lua
https://www.root.cz/clanky/objektove-orientovane-programovani-v-lua/ - Objektově orientované programování v Lua II
https://www.root.cz/clanky/objektove-orientovane-programovani-v-lua-ii/ - Programovací jazyk Lua a koprogramy
https://www.root.cz/clanky/programovaci-jazyk-lua-a-koprogramy/ - Knihovny a frameworky pro programovací jazyk Lua
https://www.root.cz/clanky/knihovny-a-frameworky-pro-programovaci-jazyk-lua/ - Lua + LÖVE: vytvořte si vlastní hru
https://www.root.cz/clanky/lua-love-vytvorte-si-vlastni-hru/ - Hrátky se systémem LÖVE
https://www.root.cz/clanky/hratky-se-systemem-love/ - Vytváříme hru v systému LÖVE
https://www.root.cz/clanky/vytvarime-hru-v-systemu-love/ - Hrátky se systémem LÖVE – částicové systémy
https://www.root.cz/clanky/hratky-se-systemem-love-casticove-systemy/ - Hrátky se systémem LÖVE – kolize a odrazy těles
https://www.root.cz/clanky/hratky-se-systemem-love-ndash-kolize-a-odrazy-teles/ - Hrátky se systémem LÖVE – kolize a odrazy těles II
https://www.root.cz/clanky/hratky-se-systemem-love-kolize-a-odrazy-teles-ii/ - Hrátky se systémem LÖVE – pružné vazby mezi tělesy
https://www.root.cz/clanky/hratky-se-systemem-love-pruzne-vazby-mezi-telesy/ - Hrátky se systémem LÖVE – dokončení
https://www.root.cz/clanky/hratky-se-systemem-love-dokonceni/ - LuaJ – implementace jazyka Lua v Javě
https://www.root.cz/clanky/luaj-ndash-implementace-jazyka-lua-v-jave/ - LuaJ a skriptování podle specifikace JSR-223
https://www.root.cz/clanky/luaj-a-skriptovani-podle-specifikace-jsr-223/ - Metalua: programovatelné rozšíření sémantiky jazyka Lua
https://www.root.cz/clanky/metalua-programovatelne-rozsireni-semantiky-jazyka-lua/ - Metalua: užitečná rozšíření jazyka Lua
https://www.root.cz/clanky/metalua-uzitecna-rozsireni-jazyka-lua/ - Programovací jazyk Lua v roli skriptovacího jazyka pro WWW stránky
https://www.root.cz/clanky/programovaci-jazyk-lua-v-roli-skriptovaciho-jazyka-pro-www-stranky/ - Interpretry, překladače, JIT překladače a transpřekladače programovacího jazyka Lua
https://www.root.cz/clanky/interpretry-prekladace-jit-prekladace-a-transprekladace-programovaciho-jazyka-lua/ - Kooperace mezi jazykem Lua a nativním (céčkovým) kódem
https://www.root.cz/clanky/kooperace-mezi-jazykem-lua-a-nativnim-ceckovym-kodem/ - Kooperace mezi jazykem Lua a nativním (céčkovým) kódem: knihovna FFI
https://www.root.cz/clanky/kooperace-mezi-jazykem-lua-a-nativnim-ceckovym-kodem-knihovna-ffi/ - Profilery pro programovací jazyk Lua
https://www.root.cz/clanky/profilery-pro-programovaci-jazyk-lua/ - Využití knihovny debug v programovacím jazyku Lua
https://www.root.cz/clanky/vyuziti-knihovny-debug-v-programovacim-jazyku-lua/ - Programovací jazyk Lua (e-book)
https://www.knihydobrovsky.cz/e-kniha/programovaci-jazyk-lua-240253190
Původně byla Lua realizována jako klasický interpret – prováděl se automatický a prakticky okamžitý překlad do bajtkódu, který byl následně interpretován. Později byl vytvořen i plnohodnotný (a nutno podotknout, že až neobvykle dobrý) just-in-time (JIT) překladač nazvaný LuaJIT. Touto zajímavou technologií jsme se zabývali v následující sérii článků (které jsou poněkud paradoxně součástí seriálu o programovacím jazyku Java a JVM):
- LuaJIT – Just in Time překladač pro programovací jazyk Lua
https://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua/ - LuaJIT – Just in Time překladač pro programovací jazyk Lua (2)
https://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-2/ - LuaJIT – Just in Time překladač pro programovací jazyk Lua (3)
https://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-3/ - LuaJIT – Just in Time překladač pro programovací jazyk Lua (4)
https://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-4/ - LuaJIT – Just in Time překladač pro programovací jazyk Lua (5 – tabulky a pole)
https://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-5-tabulky-a-pole/ - LuaJIT – Just in Time překladač pro programovací jazyk Lua (6 – překlad programových smyček do mezijazyka LuaJITu)
https://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-6-preklad-programovych-smycek-do-mezijazyka-luajitu/ - LuaJIT – Just in Time překladač pro programovací jazyk Lua (7 – dokončení popisu mezijazyka LuaJITu)
https://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-7-dokonceni-popisu-mezijazyka-luajitu/ - LuaJIT – Just in Time překladač pro programovací jazyk Lua (8 – základní vlastnosti trasovacího JITu)
https://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-8-zakladni-vlastnosti-trasovaciho-jitu/ - LuaJIT – Just in Time překladač pro programovací jazyk Lua (9 – další vlastnosti trasovacího JITu)
https://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-9-dalsi-vlastnosti-trasovaciho-jitu/ - LuaJIT – Just in Time překladač pro programovací jazyk Lua (10 – JIT překlad do nativního kódu)
https://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-10-jit-preklad-do-nativniho-kodu/ - LuaJIT – Just in Time překladač pro programovací jazyk Lua (11 – JIT překlad do nativního kódu procesorů s architekturami x86 a ARM)
https://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-11-jit-preklad-do-nativniho-kodu-procesoru-s-architekturami-x86-a-arm/ - LuaJIT – Just in Time překladač pro programovací jazyk Lua (12 – překlad operací s reálnými čísly)
https://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-12-preklad-operaci-s-realnymi-cisly/
A konečně nesmíme zapomenout na to, že kromě původní implementace interpretru a LuaJITu existuje celá řada dalších implementací tohoto programovacího jazyka. Některé z těchto implementací byly zmíněny v následujících článcích:
- Skriptovací jazyk Lua v aplikacích naprogramovaných v Go
https://www.root.cz/clanky/skriptovaci-jazyk-lua-v-aplikacich-naprogramovanych-v-go/ - Programovací jazyk Lua v roli skriptovacího jazyka pro WWW stránky
https://www.root.cz/clanky/programovaci-jazyk-lua-v-roli-skriptovaciho-jazyka-pro-www-stranky/ - LuaJ – implementace jazyka Lua v Javě
https://www.root.cz/clanky/luaj-ndash-implementace-jazyka-lua-v-jave/ - Tvorba pluginů pro Vim s využitím programovacího jazyka Lua
https://www.root.cz/clanky/tvorba-pluginu-pro-vim-s-vyuzitim-programovaciho-jazyka-lua/
20. Odkazy na Internetu
- Repositář projektu Lua Fun
https://github.com/luafun/luafun - Lua Functional 0.1.3 documentation
https://luafun.github.io/reference.html - Getting Started
https://luafun.github.io/getting_started.html - Rockspec knihovny Fun
https://raw.githubusercontent.com/luafun/luafun/master/fun-scm-1.rockspec - Awesome Lua – A curated list of quality Lua packages and resources.
https://github.com/LewisJEllis/awesome-lua - Repositář projektu Moses
https://github.com/Yonaba/Moses/ - Lambda the Ultimate: Coroutines in Lua
http://lambda-the-ultimate.org/node/438 - Coroutines Tutorial
http://lua-users.org/wiki/CoroutinesTutorial - Lua Coroutines Versus Python Generators
http://lua-users.org/wiki/LuaCoroutinesVersusPythonGenerators - Programming in Lua 9.1 – Coroutine Basics
http://www.lua.org/pil/9.1.html - Wikipedia CZ: Koprogram
http://cs.wikipedia.org/wiki/Koprogram - Wikipedia EN: Coroutine
http://en.wikipedia.org/wiki/Coroutine - Repositář knihovny Moses
https://github.com/Yonaba/Moses/ - Návod k použití knihovny Moses
https://github.com/Yonaba/Moses/blob/master/doc/tutorial.md - How to understand clojure's lazy-seq
https://stackoverflow.com/questions/44095400/how-to-understand-clojures-lazy-seq - Lua Implementations
http://lua-users.org/wiki/LuaImplementations - Generator (computer programming)
https://en.wikipedia.org/wiki/Generator_(computer_programming) - Lambda the Ultimate: Coroutines in Lua,
http://lambda-the-ultimate.org/node/438 - Coroutines Tutorial,
http://lua-users.org/wiki/CoroutinesTutorial - Lua Coroutines Versus Python Generators,
http://lua-users.org/wiki/LuaCoroutinesVersusPythonGenerators - Category:Lua na Rosetta code
https://rosettacode.org/wiki/Category:Lua - Programming in Lua: 23 – The Debug Library
http://www.lua.org/pil/23.html - Programming in Lua: 23.1 – Introspective Facilities
http://www.lua.org/pil/23.1.html - Programming in Lua: 23.2 – Hooks
http://www.lua.org/pil/23.2.html - Lua 5.2 Reference Manual: 6.10 – The Debug Library
http://www.lua.org/manual/5.2/manual.html#6.10 - Turtles all the way down
https://en.wikipedia.org/wiki/Turtles_all_the_way_down - Issues k projektu LuaFun
https://github.com/luafun/luafun/issues - Archived | Embed Lua for scriptable apps
https://developer.ibm.com/tutorials/l-embed-lua/ - Embedding Lua
https://www.oreilly.com/library/view/lua-quick-start/9781789343229/3a6f3daf-f74c-4a25-a125–584da58568e4.xhtml