Obsah
1. Lua Fun: knihovna pro zpracování konečných i nekonečných sekvencí v jazyce Lua
2. Sekvence a lazy sekvence v programovacím jazyku Clojure
3. Koncepty, na nichž je postavena knihovna Lua Fun
4. Instalace knihovny Lua Fun, první otestování její funkcionality
5. Generátory konečných i nekonečných sekvencí
6. Generátor range (konečná sekvence)
7. Generátory nekonečných sekvencí
8. Získání prvního prvku sekvence, popř. sekvence bez prvního prvku
9. Funkce pro získání n-tého prvku ze sekvence
10. Výběr podsekvencí (slicing) funkcemi taken a dropn
11. Rozdělení sekvence funkcí span/split
12. Malá odbočka – funkce vyššího řádu
13. Funkce take_while a drop_while
14. Funkce span/split s predikátem
18. Repositář s demonstračními příklady
19. Články o programovacím jazyce Lua i o technologii LuaJITu
1. Lua Fun: knihovna pro zpracování konečných i nekonečných sekvencí v jazyce Lua
Lua Fun. Simple, Efficient and Functional. In Lua. With JIT.
V dnešním článku se seznámíme se základními možnostmi knihovny nazvané poměrně příznačně Lua Fun. Jedná se o knihovnu, která do programovacího jazyka Lua přidává některé funkce používané v jazycích orientovaných na paradigma funkcionálního programování. Především se jedná o implementaci generátorů konečných i nekonečných sekvencí a taktéž o implementaci funkcí, které s těmito generátory dokážou pracovat. Jazyk Lua, jeho implementace (původní interpret i LuaJIT) a dokonce i jeho základní knihovny je pojat přísně minimalisticky (což je jedna z jeho předností v porovnání s některými mainstreamovými jazyky) a podobně je koncipována i knihovna Lua Fun, která je dodávána jako jediný soubor fun.lua, jehož čitelná varianta má velikost 29kB (a po minifikaci ještě méně). Navíc je relativně snadné z knihovny Lua Fun převzít pouze skutečně využívané generátory a funkce a vytvořit si tak vlastní variantu této knihovny (což se ostatně ve světě programovacího jazyka Lua poměrně často děje i s jinými knihovnami).
Pokud se podíváme na historii změn provedených v repositáři tohoto projektu, zjistíme, že další oficiální vývoj již neprobíhá (viz též tuto issue). Pokud z nějakého důvodu nechcete používat nepodporovanou knihovnu, existuje minimálně jedna alternativní implementace podobných principů představovaná knihovnou Moses, jejímž popisem se budeme zabývat v samostatném článku. Ovšem nespornou předností knihovny Lua Fun je její efektivita a optimalizace provedené s ohledem na LuaJIT, což je velmi úspěšný a výkonný just-in-time překladač programovacího jazyka Lua.
2. Sekvence a lazy sekvence v programovacím jazyku Clojure
Knihovna Lua Fun dokáže pracovat s konečnými sekvencemi a sekvencemi nekonečnými. Pro první typ sekvencí se někdy používá označení seznam (list), pro druhý pak proud (stream). Koncept nekonečných sekvencí sice není v oblasti programovacích jazyků žádnou žhavou novinkou, ovšem do praktické podoby byl dopracován až relativně pozdě. Velmi dobrým příkladem programovacího jazyka, jenž je na tomto konceptu do značné míry postaven, je jazyk Clojure, takže si v této kapitole dovolím malou odbočku k tomuto jazyku (ta je vhodná mj. i proto, že se seznámíme s použitými konvencemi pojmenování, protože ty ještě nejsou zcela ustálené, popř. se v každém ekosystému používají mírně odlišné termíny).
Mnoho funkcí a maker, které nalezneme ve standardní knihovně programovacího jazyka Clojure souvisí s takzvanými sekvencemi. Tímto termínem se označuje programové rozhraní, které svými základními možnostmi zhruba odpovídá iterátorům známým z programovacího jazyka Java. V Clojure existuje velké množství funkcí, které dokážou pracovat se sekvencemi, ať již se jedná o běžné sekvence (jejichž prvky jsou přímo uloženy v operační paměti), nebo takzvané líné sekvence (lazy sekvence), které nové prvky vytváří či zjišťují až při konkrétním přístupu na tyto prvky. Mezi tyto funkce patří například sort, sort-by, take či flatten. Díky tomu, že všechny standardní kolekce (seznamy, vektory, …) jsou současně i sekvencemi, lze tyto funkce aplikovat i na kolekce, ovšem ve skutečnosti jsou sekvencemi i další typy objektů, zejména pak I/O proudy (tímto směrem se posunuly i standardní knihovny Javy), řetězce (což jsou sekvence znaků) atd.
Naprostý základ pro práci se sekvencemi tvoří trojice funkcí nazvaných first, rest a next. Funkce first vrací první prvek v sekvenci, popř. speciální hodnotu nil v případě, že je sekvence prázdná. Funkce rest i next vrací zbylé prvky v sekvenci, ovšem liší se tím, jaká hodnota se vrátí ve chvíli, kdy již v sekvenci nezbyly žádné prvky (kromě prvního). V tomto případě vrátí rest prázdnou sekvenci (například prázdný seznam), zatímco funkce next vrátí již zmíněnou speciální hodnotu nil. U běžných sekvencí, například seznamů, jsou tyto funkce implementovány přímočaře, ovšem v případě lazy sekvencí se prvky vrácené pomocí funkce first vyhodnocují až za běhu, například pomocí nějaké generátorové funkce. Tímto způsobem je možné pracovat i s nekonečnými sekvencemi, u nichž už z principu nelze dopředu znát celkový počet prvků atd.
Velmi dobrým příkladem lazy sekvence je funkce range, která dokonce existuje v několika podobách, jež se od sebe z hlediska programátora-uživatele liší především různým počtem parametrů. Pokud se této funkci nepředá žádný parametr, vrátí funkce range sekvenci celých čísel od nuly do nekonečna. Zde je patrné, proč se musí jednat o lazy sekvenci – nekonečnou řadu celých čísel by samozřejmě v případě normální sekvence nebylo možné uložit do operační paměti. Pokud se funkci range předá pouze jediný parametr (kterým musí být celé číslo – je kontrolováno v runtime), je vrácena sekvence celých čísel od 0 do zadané hodnoty-1. Opět se jedná o nefalšovanou lazy sekvenci, takže se nemusíte bát používat i velké n. Dále již následují v podstatě jen kosmetické úpravy – volání funkce range se dvěma parametry m, n vytvoří sekvenci celých čísel od m do n-1 a pokud je použit ještě třetí parametr, určuje se jím krok, který může být i záporný.
Takto navrženou funkci range nalezneme například i v knihovně clj určené pro Python. Je přitom zachováno standardní chování range ze základní knihovny Pythonu, ovšem v případě potřeby může tato funkce (volaná bez parametrů) vytvořit nekonečnou lazy sekvenci! A podobně koncipovanou funkci nalezneme právě i v knihovně Lua Fun, kde vystupuje v roli generátoru.
3. Koncepty, na nichž je postavena knihovna Lua Fun
V programovacím jazyku Lua se chování skutečných sekvencí a lazy sekvencí (včetně sekvencí potenciálně nekonečných) může napodobit s využitím takzvaných generátorů. Ty lze implementovat buď ve formě koprogramu (coroutine) nebo klasického iterátoru, přičemž v knihovně Lua Fun se používají právě iterátory. Základními iterátory, které jsou součástí přímo základní knihovny programovacího jazyka Lua, jsou iterátory nazvané ipairs a pairs, které slouží pro procházení polem, resp. slovníkem (čistě náhodou jsou obě tyto struktury reprezentovány tabulkou, ale v praxi mezi nimi odlišujeme). Tyto iterátory se typicky používají v programové smyčce for, ovšem můžeme je použít i samostatně:
> ipairs({}) function: 0x7f6f957b1940 table: 0x55c57a1fcb50 0 > pairs({}) function: 0x7f6f957b1820 table: 0x55c57a1fcdc0 nil
Můžeme vidět, že oba konstruktory vrací trojici hodnot. Konkrétně se jedná o generující funkci (tedy o vlastní generátor), která po svém zavolání vrátí další hodnotu v sekvenci. Tato funkce většinou po svém zavolání mění interní stav generátoru (počitadlo atd.). Další hodnotou je (většinou) tabulka s počátečním stavem iterátoru a hodnotou třetí pak stav iterátoru.
Chování si můžeme relativně snadno odzkoušet. Nejdříve všechny tři hodnoty uložíme do pomocných proměnných:
> gen, param, state = ipairs({'a', 'b', 'c'})
Následně můžeme explicitně generující funkci zavolat a předat jí počáteční stav a aktuální stav iterátoru:
> gen(param, state) 1 a
Vidíme, že tato funkce vrátí nový stav a současně i hodnotu, kvůli které jsme iterátor vytvářeli – první prvek ze sekvence.
Můžeme pochopitelně odsimulovat i další kroky iterační smyčky:
> gen(param, 1) 2 b > gen(param, 2) 3 c > gen(param, 3) nil
V posledním kroku se pouze vrátila hodnota nil značící konec sekvence vytvářené iterátorem.
Prakticky stejným způsobem lze vytvářet i další generátory tvořící konečné nebo nekonečné sekvence. Následuje úryvek kódu získaný přímo z knihovny Lua Fun, který ukazuje, jak je možné s takovým generátorem pracovat (povšimněte si zejména funkce wrap):
local iterator_mt = { -- usually called by for-in loop __call = function(self, param, state) return self.gen(param, state) end; __tostring = function(self) return '<generator>' end; -- add all exported methods __index = methods; } local wrap = function(gen, param, state) return setmetatable({ gen = gen, param = param, state = state }, iterator_mt), param, state end exports.wrap = wrap local unwrap = function(self) return self.gen, self.param, self.state end methods.unwrap = unwrap
S pomocí funkce wrap (což je mimochodem funkce vyššího řádu) lze implementovat například generátor nazvaný duplicate, který generuje nekonečnou sekvenci stejných hodnot:
local duplicate_table_gen= function(param_x, state_x) return state_x + 1, unpack(param_x) end local duplicate_gen = function(param_x, state_x) return state_x + 1, param_x end local duplicate = function(...) if select('#', ...) <= 1 then return wrap(duplicate_gen, select(1, ...), 0) else return wrap(duplicate_table_gen, {...}, 0) end end
4. Instalace knihovny Lua Fun, první otestování její funkcionality
Konkrétní průběh instalace knihovny Lua Fun závisí na tom, zda je v systému nainstalován správce balíčků programovacího jazyka Lua, který se jmenuje LuaRocks. Pokud tohoto správce balíčků používáte, lze pro instalaci Lua Fun použít specifikaci balíčku, která je umístěna na adrese https://raw.githubusercontent.com/luafun/luafun/master/fun-scm-1.rockspec. V případě, že jako mnoho jiných programátorů používajících jazyk Lua, si chcete balíčky instalovat sami ručně, je to v tomto případě velmi snadné – postačuje naklonovat příslušný repositář, přesunout se do adresáře s projektem a otestovat, zda modul fun.lua pracuje podle očekávání:
$ git clone git://github.com/luafun/luafun.git $ cd luafun $ cd tests $ ./runtest *.lua Testing basic.lua Testing compositions.lua Testing filtering.lua Testing generators.lua Testing indexing.lua Testing operators.lua Testing reducing.lua Testing slicing.lua Testing transformations.lua All tests have passed!
Dále si otestujeme základní funkcionalitu tohoto balíčku. Přesuňte se do adresáře, v němž se nachází soubor fun.lua, popř. si tento soubor umístěte do adresáře, na který odkazuje proměnná LUA_PATH. V dalším kroku spustíme interpret jazyka Lua:
$ lua Lua 5.3.4 Copyright (C) 1994-2017 Lua.org, PUC-Rio
Načteme modul a vrácený objekt spustíme (proto jsou na konci příkazu uvedeny kulaté závorky). Tímto trikem se všechny funkce z balíčku stanou součástí globálního jmenného prostoru:
> require "fun"()
Nyní můžeme otestovat existenci nějaké funkce, například generátoru range popsaného níže:
> range(10) <generator> table: 0x5642ba54b010 0
Totéž lze provést s LuaJITem, protože knihovna Lua Fun je optimalizována právě takovým způsobem, aby byla v LuaJITu velmi rychlá:
$ luajit LuaJIT 2.1.0-beta3 -- Copyright (C) 2005-2017 Mike Pall. http://luajit.org/ JIT: ON SSE2 SSE3 SSE4.1 BMI2 fold cse dce fwd dse narrow loop abc sink fuse
Načtení modulu s jeho otestováním:
> require "fun"() > range(10)
-- načtení knihovny Lua Fun fun = require "fun" -- pokus o použití funkce z knihovny Lua Fun print(fun.range(10))
Alternativně je možné do globálního jmenného prostoru přidat funkce ručně:
-- načtení knihovny Lua Fun a současně import symbolů do globálního jmenného prostoru for k, v in pairs(require "fun") do _G[k] = v end -- pokus o použití funkce z knihovny Lua Fun print(range(10))
5. Generátory konečných i nekonečných sekvencí
O vytváření sekvencí se v knihovně Lua Fun starají generátory zmíněné v předchozím textu. Některé z generátorů vytváří konečné sekvence, další sekvence (potenciálně) nekonečné, což znamená, že prvky takové sekvence jsou generovány až na požádání (současně se tedy jedná o sekvence líné neboli lazy):
# | Generátor | Sekvence | Stručný popis |
---|---|---|---|
1 | range | konečná | série číselných hodnot mezi zadanými mezemi s určitým krokem mezi hodnotami |
2 | zeros | nekonečná | série nulových hodnot |
3 | ones | nekonečná | série jedniček |
4 | xrepeat | nekonečná | sekvence jedné opakující se hodnoty |
5 | duplicate | nekonečná | alias pro předchozí generátor |
6 | tabulate | nekonečná | sekvence generovaná nějakou funkcí nebo uzávěrem |
Pojďme si nyní na několika příkladech ukázat základy práce s generátory. Nejdříve spustíme interpret programovacího jazyka Lua:
$ lua Lua 5.3.4 Copyright (C) 1994-2017 Lua.org, PUC-Rio >
Dále naimportujeme všechny funkce deklarované v modulu „fun“. Tento příkaz je nutné spouštět v adresáři, v němž je umístěn soubor „fun.lua“, popř. je možné (alternativně) nastavit cestu v proměnné prostředí LUA_PATH přímo v interpretru programovacího jazyka Lua:
> require "fun"()
Dále přímo v interpretru vytvoříme generátor a necháme si vypsat jeho hodnotu (což je tabulka – ovšem tabulkou může být i plnohodnotný objekt):
> range(10) <generator> table: 0x556eee33bfc0 0
Funkcí totable můžeme generátor zkonvertovat na skutečnou tabulku s hodnotami:
> totable(range(10)) table: 0x556eee32a270
Získání počtu prvků zajistí operátor #:
> #totable(range(10)) 10
Tabulkou můžeme projít s využitím programové smyčky for a vypsat jak indexy prvků, tak i jejich hodnoty:
for index, value in ipairs(totable(range(10))) do print(index, value) end 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10
Další možnost průchodu, tentokrát s využitím iterátoru:
for index, value in iter(range(10)) do print(index, value) end 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10
Pro zjednodušení dále popsaných demonstračních příkladů použijeme následující funkci, která vypíše obsah sekvence, pokud se tedy nejedná o sekvenci nekonečnou:
function printSequence(sequence) for index, value in iter(sequence) do print(index, value) end end
Tuto funkci si můžeme velmi snadno otestovat:
> printSequence(range(10)) 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10
6. Generátor range (konečná sekvence)
Jedním z nejužitečnějších generátorů, které v knihovně Lua Fun nalezneme, je generátor nazvaný range. Jméno tohoto generátoru není zvoleno náhodně, ale je do určité míry konzistentní s programovacím jazykem Python, protože i zde má generátor range dosti podobné vlastnosti (s tou výjimkou, že v jazyce Lua počítáme od jedničky, liší se práce s mezními hodnotami a range implementovaná v jazyce Lua je více konzistentní). Ukažme si tedy základní způsoby použití tohoto generátoru.
Pokud generátoru předáme jedinou kladnou hodnotu, budou se generovat celá čísla od jedničky až do této hodnoty (včetně):
printSequence(range(10)) 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10
V případě, že předáme hodnotu zápornou, bude chování mírně odlišné:
printSequence(range(10)) -1 -1 -2 -2 -3 -3 -4 -4 -5 -5 -6 -6 -7 -7 -8 -8 -9 -9 -10 -10
Samozřejmě nám nic nebrání procházet přímo generovanými hodnotami, což je prakticky stejný zápis, jaký známe z Pythonu:
for index, value in range(10) do print(index, value) end 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10
Specifikace počáteční i koncové hodnoty sekvence:
printSequence(range(0, 10)) 0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10
Specifikace kroku, pokud je odlišný od výchozí hodnoty 1:
printSequence(range(0, 10, 2)) 0 0 2 2 4 4 6 6 8 8 10 10
Počítání od vyšších hodnot směrem k nižším nutně nevyžaduje specifikaci kroku (poměrně užitečné chování):
printSequence(range(10, 0)) 10 10 9 9 8 8 7 7 6 6 5 5 4 4 3 3 2 2 1 1 0 0
V případě, že je krok odlišný od 1 nebo –1, musíme ho explicitně uvést:
printSequence(range(10, 0, -2)) 10 10 8 8 6 6 4 4 2 2 0 0
Podporovány jsou i mezní hodnoty a krok, který není celým číslem:
printSequence(range(0.5, 10, 0.5)) 0.5 0.5 1.0 1.0 1.5 1.5 2.0 2.0 2.5 2.5 3.0 3.0 3.5 3.5 4.0 4.0 4.5 4.5 5.0 5.0 5.5 5.5 6.0 6.0 6.5 6.5 7.0 7.0 7.5 7.5 8.0 8.0 8.5 8.5 9.0 9.0 9.5 9.5 10.0 10.0
Otestujme si nyní potenciálně chybný příklad s problematickou hodnotou kroku 0.1:
printSequence(range(0.0, 1.0, 0.1)) 0.0 0.0 0.1 0.1 0.2 0.2 0.3 0.3 0.4 0.4 0.5 0.5 0.6 0.6 0.7 0.7 0.8 0.8 0.9 0.9 1.0 1.0
Stejná operace, ovšem rozepsána do programové smyčky:
for index, value in range(0.0, 1.0, 0.1) do print(index, value) end 0.0 0.0 0.1 0.1 0.2 0.2 0.3 0.3 0.4 0.4 0.5 0.5 0.6 0.6 0.7 0.7 0.8 0.8 0.9 0.9 1.0 1.0
Pokus o použití nulové hodnoty kroku vede k chybě:
for i, v in range(0, 10, 0) do print(i, v) end ./fun.lua:221: step must not be zero stack traceback: [C]: in function 'assert' ./fun.lua:221: in function 'range' stdin:1: in main chunk [C]: in ?
7. Generátory nekonečných sekvencí
V této kapitole se seznámíme s generátory nekonečných sekvencí. Tyto sekvence pochopitelně není možné celé vyhodnotit ani vypsat, proto se zpracovávají s využitím filtrace, řezů atd.:
# | Generátor | Stručný popis |
---|---|---|
1 | zeros | nekonečná série nulových hodnot |
2 | ones | nekonečná série jedniček |
3 | xrepeat | nekonečná sekvence jedné opakující se hodnoty |
4 | duplicate | alias pro předchozí generátor |
5 | tabulate | nekonečná sekvence generovaná nějakou funkcí nebo uzávěrem |
Nejprve otestujeme, že generátory lze vytvořit:
> zeros() <generator> 0 0 > ones() <generator> 1 0 > xrepeat(42) <generator> 42 0 > xrepeat("Hello") <generator> Hello 0 > duplicate(42) <generator> 42 0 > duplicate("Hello") <generator> Hello 0
Vytvoření sekvence stejných hodnot s jejich základním zpracováním (výpisem):
i=10 for index, value in duplicate("Hello") do print(index, value) i = i - 1 if i == 0 then break end end 1 Hello 2 Hello 3 Hello 4 Hello 5 Hello 6 Hello 7 Hello 8 Hello 9 Hello 10 Hello
Otestování chování ostatních generátorů (kromě tabulate):
-- 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 print("zeros()") print("-----------------------------------") printSequence(zeros(), 10) print() print("ones()") print("-----------------------------------") printSequence(ones(), 10) print() print("xrepeat(42)") print("-----------------------------------") printSequence(xrepeat(42), 10) print() print("xrepeat('hello')") print("-----------------------------------") printSequence(xrepeat('hello'), 10) print() print("xrepeat(nil)") print("-----------------------------------") printSequence(xrepeat(nil), 10) print() print("duplicate(42)") print("-----------------------------------") printSequence(duplicate(42), 10) print() print("duplicate('hello')") print("-----------------------------------") printSequence(duplicate('hello'), 10) print() print("duplicate(nil)") print("-----------------------------------") printSequence(duplicate(nil), 10)
S výsledky:
zeros() ----------------------------------- 1 0 2 0 3 0 4 0 5 0 6 0 7 0 8 0 9 0 10 0 ones() ----------------------------------- 1 1 2 1 3 1 4 1 5 1 6 1 7 1 8 1 9 1 10 1 xrepeat(42) ----------------------------------- 1 42 2 42 3 42 4 42 5 42 6 42 7 42 8 42 9 42 10 42 xrepeat('hello') ----------------------------------- 1 hello 2 hello 3 hello 4 hello 5 hello 6 hello 7 hello 8 hello 9 hello 10 hello xrepeat(nil) ----------------------------------- 1 nil 2 nil 3 nil 4 nil 5 nil 6 nil 7 nil 8 nil 9 nil 10 nil duplicate(42) ----------------------------------- 1 42 2 42 3 42 4 42 5 42 6 42 7 42 8 42 9 42 10 42 duplicate('hello') ----------------------------------- 1 hello 2 hello 3 hello 4 hello 5 hello 6 hello 7 hello 8 hello 9 hello 10 hello duplicate(nil) ----------------------------------- 1 nil 2 nil 3 nil 4 nil 5 nil 6 nil 7 nil 8 nil 9 nil 10 nil
Konstrukce generátoru tabulate:
> tabulate(function(x) return x*x end) <generator> function: 0x55612b716780 0
Použití generátoru tabulate pro vygenerování druhých mocnin hodnot 0 až n:
for index, value in tabulate(function(x) return x*x end) do print(index, value) if value > 100 then break end end 1 0 2 1 3 4 4 9 5 16 6 25 7 36 8 49 9 64 10 81 11 100 12 121
Do generátoru tabulate pochopitelně můžeme předat i pojmenovanou (neanonymní) funkci:
function factorial(n) for i=1,n-1 do n=n*i end return n end
A vypočítat faktoriály pro prvních deset přirozených čísel:
for n, fact in tabulate(factorial) do print(n, fact) if fact > 100000 then break end end 1 0 2 1 3 2 4 6 5 24 6 120 7 720 8 5040 9 40320 10 362880
8. Získání prvního prvku sekvence, popř. sekvence bez prvního prvku
V úvodních kapitolách jsme si řekli, že v programovacím jazyku Clojure, který zde chápeme jako etalon práce se sekvencemi, je celá koncepce založena na trojici funkcí nazvaných first, rest a next. Funkce first vrací první prvek sekvence, funkce rest novou sekvenci bez prvního prvku (ovšem interně se pochopitelně nemusí provádět kopie původní sekvence) a next se používá při průchodu sekvencí (což ovšem není téma této kapitoly). Funkce first a rest jsou skutečně naprostým základem a nalezneme je i v knihovně Lua Fun:
# | Funkce v Clojure | Obdoba v Lua Fun | Alternativní název |
---|---|---|---|
1 | first | head | car |
2 | rest | tail | cdr |
Následují příklady použití funkce head, která zpracovává i běžné tabulky jazyka Lua:
> head({1,2,3}) 1 > head(range(10)) 1 > head(duplicate("hello")) hello > head(tabulate(function (x) return x+1 end)) 1
Navíc je prováděna kontrola, jestli není sekvence prázdná – pro takovou sekvenci nelze získat hlavičku:
> head({}) ./fun.lua:323: head: iterator is empty stack traceback: [C]: in function 'error' ./fun.lua:323: in function <./fun.lua:321> (...tail calls...) [C]: in ?
Příklady použití funkce tail, a to včetně aplikace na prázdnou sekvenci či tabulku (což je povoleno):
> tail({}) <generator> nil nil > tail({1,2,3}) <generator> table: 0x55612b7276d0 1 > tail(tabulate(function (x) return x+1 end)) <generator> function: 0x55612b7267d0 1 > head(tail({1,2,3})) 2 > head(tail(tail({1,2,3}))) 3 > tail("Hello world!") <generator> Hello world! 1 > head(tail("Hello world!")) e
9. Funkce pro získání n-tého prvku ze sekvence
Další funkce, s níž se dnes seznámíme, dokáže ze sekvence vybrat n-tý prvek. Přitom musíme mít na paměti, že v programovacím jazyce Lua se prvky v polích indexují od jedničky a nikoli od nuly (což má své klady, ale i zápory, na které dříve či později narazí především programátoři používající jazyky z céčkové větve vývoje programovacích jazyků). Z tohoto důvodu má první prvek v sekvenci index roven jedné atd. Otestování je snadné:
> nth(2, {10,20,30,40}) 20 > nth(100, {10,20,30,40}) nil > nth(3, zeros()) 0 > nth(42, range(0, 100)) 41 > nth(42, range(0, 10)) nil
Další příklad použití kombinuje nth a tail:
> nth(42, tail(range(0, 100))) 42
Sekvencemi jsou i běžné řetězce, takže:
> nth(1, "Hello world!") H > nth(5, "Hello world!") o
Pokus o použití záporného indexu (neindexuje se tedy od konce sekvence):
> nth(-5, "Hello world!") ./fun.lua:299: invalid first argument to nth stack traceback: [C]: in function 'assert' ./fun.lua:299: in function <./fun.lua:298> (...tail calls...) [C]: in ?
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
10. Výběr podsekvencí (slicing) funkcemi taken a dropn
Kromě výběru jediného prvku lze provést i řez sekvence (slice) a získat prvních n prvků, popř. naopak podsekvenci bez prvních n prvků. Pro tento účel slouží dvojice funkcí pojmenovaných taken a dropn. Kromě toho lze použít i univerzálnější funkce nazvané take a drop, kterým se předává buď celé číslo (index), popř. predikát (viz též další kapitoly):
# | Funkce | Stručný popis |
---|---|---|
1 | taken | získání prvních n prvků ze sekvence |
2 | take | získání prvních n prvků ze sekvence pokud je prvním parametrem celé číslo |
3 | dropn | přeskočení prvních n prvků ze sekvence |
4 | drop | přeskočení prvních n prvků ze sekvence pokud je prvním parametrem celé číslo |
Otestování základních vlastností funkcí taken a dropn si ukážeme na dalším demonstračním příkladu, jehož zdrojový kód vypadá 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 printSequence(sequence) for _, value in iter(sequence) do print(value) end end g = range(1, 10) print("Original sequence") print("----------------------") printSequence(g) print() print("take_n(5, sequence)") print("----------------------") printSequence(take_n(5, g)) print() print("take_n(100, sequence)") print("----------------------") printSequence(take_n(100, g)) print() print("take_n(0, sequence)") print("----------------------") printSequence(take_n(0, g)) print() print("drop_n(5, sequence)") print("----------------------") printSequence(drop_n(5, g)) print() print("drop_n(100, sequence)") print("----------------------") printSequence(drop_n(100, g)) print() print("drop_n(0, sequence)") print("----------------------") printSequence(drop_n(0, g))
S výsledky:
Original sequence ---------------------- 1 2 3 4 5 6 7 8 9 10 take_n(5, sequence) ---------------------- 1 2 3 4 5 take_n(100, sequence) ---------------------- 1 2 3 4 5 6 7 8 9 10 take_n(0, sequence) ---------------------- drop_n(5, sequence) ---------------------- 6 7 8 9 10 drop_n(100, sequence) ---------------------- drop_n(0, sequence) ---------------------- 1 2 3 4 5 6 7 8 9 10
Stejných výsledků dosáhneme ve chvíli, kdy použijeme funkce take a drop, přičemž v prvním parametru předáme index, tedy celé číslo (a nikoli predikát):
-- 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 g = range(1, 10) print("Original sequence") print("----------------------") printSequence(g) print() print("take(5, sequence)") print("----------------------") printSequence(take(5, g)) print() print("take(100, sequence)") print("----------------------") printSequence(take(100, g)) print() print("take(0, sequence)") print("----------------------") printSequence(take(0, g)) print() print("drop(5, sequence)") print("----------------------") printSequence(drop(5, g)) print() print("drop(100, sequence)") print("----------------------") printSequence(drop(100, g)) print() print("drop(0, sequence)") print("----------------------") printSequence(drop(0, g))
11. Rozdělení sekvence funkcí span/split
Spojením funkcí take a drop vznikne nová funkce, která se jmenuje span nebo taktéž split (v knihovně Lua Fun existují oba tyto aliasy). Této funkci se předává celé číslo (index) n a sekvence. Výsledkem volání jsou dvě hodnoty, přičemž první hodnota odpovídá volání taken(n, sekvence) a druhá hodnota odpovídá dropn(n, sekvence) (Lua patří mezi ty programovací jazyky, které dokážou z funkce vrátit více hodnot, což je v praxi velmi užitečné). Podívejme se nyní na příklad použití funkce span/split:
-- 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 g = range(1, 10) print("Original sequence") print("---------------------------------------------------") printSequence(g) print() print("split(5, sequence)") print("---------------------------------------------------") s1, s2 = split(5, g) printSequence(s1) print("-- split --") printSequence(s2) print() print("split(0, sequence)") print("---------------------------------------------------") s1, s2 = split(0, g) printSequence(s1) print("-- split --") printSequence(s2) print() print("split(100, sequence)") print("---------------------------------------------------") s1, s2 = split(100, g) printSequence(s1) print("-- split --") printSequence(s2)
Po spuštění se nejdříve vypíše obsah původní sekvence a poté sekvence rozdělené funkcí split. Povšimněte si, že opět nezáleží na tom, zda index (prvku na hranici) leží v sekvenci či nikoli:
Original sequence --------------------------------------------------- 1 2 3 4 5 6 7 8 9 10 split(5, sequence) --------------------------------------------------- 1 2 3 4 5 -- split -- 6 7 8 9 10 split(0, sequence) --------------------------------------------------- -- split -- 1 2 3 4 5 6 7 8 9 10 split(100, sequence) --------------------------------------------------- 1 2 3 4 5 6 7 8 9 10 -- split --
12. Malá odbočka – funkce vyššího řádu
Zajímavé a užitečné je použití takzvaných funkcí vyššího řádu neboli higher-order functions. Jedná se o funkce, které jako svůj parametr (nebo parametry) akceptují jiné funkce, popř. nějaké funkce vrací ve své návratové hodnotě (nebo hodnotách – Lua totiž dokáže z funkce vrátit více hodnot). Vzhledem k tomu, že funkce jsou v programovacím jazyku Lua plnohodnotnými datovými typy, podporuje tento jazyk i použití funkcí vyššího řádu. V praxi to znamená, že prakticky libovolnou funkci můžeme předat jako parametr jiné funkci, nějaká funkce může jako svoji návratovou hodnotu (hodnoty) vracet jinou funkci apod. Současně programovací jazyk Lua podporuje i takzvané uzávěry (closure), což jsou funkce, na které je navázáno i jejich prostředí (environment), typicky nějaké proměnné. S tímto konceptem, který je v knihovně Lua Fun taktéž použit, se blíže seznámíme v navazující části tohoto článku. Dnes se setkáme „pouze“ s vybranými funkcemi vyššího řádu, mezi něž patří:
# | Funkce | Stručný popis |
---|---|---|
1 | take | získání prvků ze začátku sekvence na základě zadané podmínky či na základě zadání počtu prvků |
2 | take_while | získání prvků ze začátku sekvence na základě zadané podmínky |
3 | drop | získání nové sekvence získané přeskočením prvků na začátku na základě podmínky či počtu prvků |
4 | drop_while | získání nové sekvence získané přeskočením prvků na začátku základě podmínky |
5 | split | rozdělení sekvence na dvě sekvence odpovídající výsledku take_while a drop_while |
6 | filter | výběr prvků ze sekvence na základě kritérií testovaných predikátem |
7 | remove_if | alias pro předchozí funkci |
8 | partition | kombinace funkce filter a stejné funkce, ovšem s opačnou podmínkou v predikátu |
9 | map | aplikace nějaké funkce na všechny prvky sekvence |
Ukažme si příklad použití funkce vyššího řádu nazvané take, jejímž prvním argumentem je funkce, zde konkrétně anonymní funkce:
take(function(x) return x <= 42 end, range(0, 100, 2))
13. Funkce take_while a drop_while
Ze sekvencí (a to i ze sekvencí nekonečných) je možné získat začátek či naopak zbytek sekvence (potenciálně nekonečný zbytek) s využitím funkcí nazvaných take_while a drop_while. Těmto funkcím je zapotřebí v prvním parametru předat takzvaný predikát určující, zda prvek splňuje nějakou podmínku (ovšem pozor – chování je odlišné od funkce filter popsané příště). V případě prvního parametru předávaného do take_while a drop_while se jedná o běžnou funkci, popř. o funkci anonymní, která by měla akceptovat jeden parametr (hodnotu prvku ze sekvence) a vracet by měla pravdivostní hodnotu true či false, popř. nil, který má v kontextu pravdivostních hodnot stejný význam jako true. Klasický příklad využívající anonymní funkci:
take_while(function(x) return x <= 5 end, range(100))
Stejný příklad, ovšem používající funkci pojmenovanou (tedy neanonymní), by vypadal následovně:
function less_that_5(x) return x <= 5 end take_while(less_that_5, range(100))
Více příkladů volajících funkce take_while a drop_while je použito v následujícím zdrojovém kódu:
-- 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 g = range(1, 10) print("Original sequence") print("---------------------------------------------------") printSequence(g) print() print("take_while(function(x) return x <= 5 end, sequence)") print("---------------------------------------------------") printSequence(take_while(function(x) return x <= 5 end, g)) print() print("take_while(function(x) return true end, sequence)") print("---------------------------------------------------") printSequence(take_while(function(x) return true end, g)) print() print("take_while(function(x) return nil end, sequence)") print("---------------------------------------------------") printSequence(take_while(function(x) return nil end, g)) print() print("drop_while(function(x) return x <= 5 end, sequence)") print("---------------------------------------------------") printSequence(drop_while(function(x) return x <= 5 end, g)) print() print("drop_while(function(x) return true end, sequence)") print("---------------------------------------------------") printSequence(drop_while(function(x) return true end, g)) print() print("drop_while(function(x) return nil end, sequence)") print("---------------------------------------------------") printSequence(drop_while(function(x) return nil end, g))
S výsledky:
Original sequence --------------------------------------------------- 1 2 3 4 5 6 7 8 9 10 take_while(function(x) return x <= 5 end, sequence) --------------------------------------------------- 1 2 3 4 5 take_while(function(x) return true end, sequence) --------------------------------------------------- 1 2 3 4 5 6 7 8 9 10 take_while(function(x) return nil end, sequence) --------------------------------------------------- drop_while(function(x) return x <= 5 end, sequence) --------------------------------------------------- 6 7 8 9 10 drop_while(function(x) return true end, sequence) --------------------------------------------------- drop_while(function(x) return nil end, sequence) --------------------------------------------------- 1 2 3 4 5 6 7 8 9 10
Opět platí, že je možné pro stejné operace použít i funkce take a while, ovšem jen za předpokladu, že v prvním parametru těchto funkcí nebude zadán index, ale predikát (tedy jméno neanonymní funkce nebo tělo funkce anonymní):
-- 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 g = range(1, 10) print("Original sequence") print("---------------------------------------------------") printSequence(g) print() print("take(function(x) return x <= 5 end, sequence)") print("---------------------------------------------------") printSequence(take(function(x) return x <= 5 end, g)) print() print("take(function(x) return true end, sequence)") print("---------------------------------------------------") printSequence(take(function(x) return true end, g)) print() print("take(function(x) return nil end, sequence)") print("---------------------------------------------------") printSequence(take(function(x) return nil end, g)) print() print("drop(function(x) return x <= 5 end, sequence)") print("---------------------------------------------------") printSequence(drop(function(x) return x <= 5 end, g)) print() print("drop(function(x) return true end, sequence)") print("---------------------------------------------------") printSequence(drop(function(x) return true end, g)) print() print("drop(function(x) return nil end, sequence)") print("---------------------------------------------------") printSequence(drop(function(x) return nil end, g))
14. Funkce span/split s predikátem
O funkci span, resp. o jejím jmenném aliasu split jsme se již zmínili v jedenácté kapitole. Nyní si tedy pouze v krátkosti uveďme příklad použití stejné funkce, ovšem s tím rozdílem, že se jí namísto indexu prvku (tedy místa, kde má dojít k rozdělení sekvence) předá predikát – anonymní či neanonymní funkce vracející pravdivostní hodnoty. Základní chování zůstane zachováno, tj. vrátí se dvě sekvence, které by po spojení měly vypadat stejně, jako sekvence původní:
-- 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 g = range(1, 10) print("Original sequence") print("---------------------------------------------------") printSequence(g) print() print("split(function(x) return x <= 5 end, sequence)") print("---------------------------------------------------") s1, s2 = split(function(x) return x <= 5 end, g) printSequence(s1) print("-- split --") printSequence(s2) print() print("split(function(x) return true end, sequence)") print("---------------------------------------------------") s1, s2 = split(function(x) return true end, g) printSequence(s1) print("-- split --") printSequence(s2) print() print("split(function(x) return nil end, sequence)") print("---------------------------------------------------") s1, s2 = split(function(x) return nil end, g) printSequence(s1) print("-- split --") printSequence(s2)
Výsledek získaný spuštěním předchozího příkladu:
Original sequence --------------------------------------------------- 1 2 3 4 5 6 7 8 9 10 split(function(x) return x <= 5 end, sequence) --------------------------------------------------- 1 2 3 4 5 -- split -- 6 7 8 9 10 split(function(x) return true end, sequence) --------------------------------------------------- 1 2 3 4 5 6 7 8 9 10 -- split -- split(function(x) return nil end, sequence) --------------------------------------------------- -- split -- 1 2 3 4 5 6 7 8 9 10
15. Funkce zip
Velmi užitečnou funkcí, kterou kromě knihovny Lua Fun nalezneme i v mnoha dalších knihovnách podporujících práci se sekvencemi (nebo streamy), je funkce nazvaná zip. Tato funkce dokáže „zazipovat“ dvě sekvence, popř. v případě potřeby i větší množství sekvencí a to takovým způsobem, že první prvek výsledné sekvence bude obsahovat první prvky ze všech sekvencí vstupních. Co se však stane ve chvíli, kdy „zipujeme“ sekvence s různým počtem prvků, popř. sekvenci konečnou se sekvencí nekonečnou? V takovém případě bude nová sekvence obsahovat takový počet prvků, jako nejkratší vstupní sekvence – což nám mj. umožní bez problémů pracovat i se sekvencemi nekonečnými.
g1 = range(1, 10) g2 = take_n(10, xrepeat("*")) z1 = zip(g1, g2)
Ale též lze provést spojení s nekonečnou sekvencí; vše bude bez problémů fungovat bez potřeby nekonečné paměti a/nebo strojového času:
g1 = range(1, 10) g2 = xrepeat("*") z1 = zip(g1, g2)
Zazipování tří sekvencí do sekvence jediné:
g1 = range(1, 10) g2 = xrepeat("*") g3 = range(10, 1) z2 = zip(g1, g2, g3)
V demonstračním příkladu, který je ukázán v navazující kapitole, používáme následující trik pro iteraci všemi prvky výsledné sekvence v případě, že tato sekvence vznikla spojením dvou vstupních sekvencí:
for _, a, b, in iter(z2) do print(a, b) end
Alternativa pro případ, že sekvence vznikla spojením (zazipováním) tří sekvencí:
for _, a, b, c in iter(z2) do print(a, b, c) end
16. Ukázka použití funkce zip
Podívejme se nyní na několik způsobů použití funkce zip, kterou jsme si ve stručnosti popsali v předchozí kapitole:
-- 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 g1 = range(1, 10) print("First original sequence") print("---------------------------------------------------") printSequence(g1) g2 = take_n(10, xrepeat("*")) print() print("Second original sequence") print("---------------------------------------------------") printSequence(g2) z1 = zip(g1, g2) print() print("Zipped sequence") print("---------------------------------------------------") for _, a, b in iter(z1) do print(a, b) end g3 = xrepeat(-1) print() print("Third original sequence (sliced)") print("---------------------------------------------------") printSequence(take_n(10, g3)) z2 = zip(g1, g2, g3) print() print("Zipped sequence") print("---------------------------------------------------") for _, a, b, c in iter(z2) do print(a, b, c) end
Výsledky získané po spuštění tohoto demonstračního příkladu by měly vypadat následovně:
First original sequence --------------------------------------------------- 1 2 3 4 5 6 7 8 9 10 Second original sequence --------------------------------------------------- * * * * * * * * * * Zipped sequence --------------------------------------------------- 1 * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * Third original sequence (sliced) --------------------------------------------------- -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 Zipped sequence --------------------------------------------------- 1 * -1 2 * -1 3 * -1 4 * -1 5 * -1 6 * -1 7 * -1 8 * -1 9 * -1 10 * -1
17. Obsah druhé části článku
Knihovna Lua Fun obsahuje ještě mnohem více užitečných a prakticky použitelných funkcí. S popisem použití těchto funkcí i s demonstračními příklady, kde budou tyto funkce použity, se setkáme v navazujícím článku. Dále se budeme zabývat knihovnou s podobným zaměřením, která je stále aktivně vyvíjena. Tato knihovna se jmenuje Moses a bude jí věnován samostatný článek.
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. 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ů 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ů:
- 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