Hlavní navigace

Lua Fun: knihovna pro zpracování konečných i nekonečných sekvencí v jazyce Lua

21. 7. 2020
Doba čtení: 37 minut

Sdílet

Sekvence, a to včetně sekvencí nekonečných, jsou velmi užitečnou datovou abstrakcí, s níž jsme se již nesčetněkrát setkali zejména při popisu jazyka Clojure. Podobný koncept ovšem můžeme využít i v jazyce Lua.

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

15. Funkce zip

16. Ukázka použití funkce zip

17. Obsah druhé části článku

18. Repositář s demonstračními příklady

19. Články o programovacím jazyce Lua i o technologii LuaJITu

20. Odkazy na Internetu

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).

Poznámka: když už pro nic jiného může být knihovna Lua Fun užitečná z toho důvodu, že obsahuje funkci range, která pracuje jako generátor sekvence celých či reálných čísel (na druhou stranu však má Lua pěknou syntaxi zápisu počítané programové smyčky, na rozdíl od Pythonu, kde se naopak musí range často (zne)užívat právě pro implementaci počítaných smyček).

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 restnext 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.githubusercon­tent.com/luafun/luafun/mas­ter/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)
Poznámka: pokud se modul pouze načte, je nutné při volání funkcí použít plné jméno, včetně jména modulu:
-- 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"()
Poznámka: povšimněte si kulatých závorek, které navíc provedou import všech funkcí přímo do globálního jmenného prostoru, takže se při volání funkcí nemusí uvádět jméno modulu s tečkou.

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
Poznámka: Python se zde chová značně odlišně!

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
Poznámka: povšimněte si, že hodnota nil je zde zpracovávána stejně, jako jiné hodnoty.

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
Poznámka: alternativní názvy jsou odvozeny od názvů funkcí, které byly použity v první reálné implementaci programovacího jazyka LISP. Původ těchto názvů je odvozen z názvů makroinstrukcí sálového počítače IBM 704, kde sloužily k přístupu k jednotlivým částem 36bitového slova těchto mainframů. Jedná se vlastně o určitý paradox – jeden z nejabstraktnějších prakticky nasaditelných programovacích jazyků používá (používal) názvy odvozené od jedné konkrétní implementace.

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
Poznámka: povšimněte si, že v posledním příkladu se vrátila hodnota nil, a to z toho důvodu, že čtyřicátý druhý prvek v sekvenci obsahující hodnoty 0 až 11, neexistuje.

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 ?
Poznámka: tato funkce sice na první pohled vypadá velmi jednoduše, ovšem u generovaných sekvencí je nutné se doiterovat až k požadovanému prvku, což pochopitelně může zvýšit časovou složitost. Ostatně se stačí podívat na zdrojový kód této funkce, v níž je iterace jasně viditelná:
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
Poznámka: povšimněte si, jaký je význam nulového indexu – funkce taken v tomto případě vrátí prázdnou sekvenci, zatímco funkce dropn sekvenci původní.

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 --
Poznámka: touto funkcí lze rozdělit například i řetězec, což si ukážeme příště.

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
Poznámka: ve skutečnosti jakákoli funkce z knihovny Lua Fun, která jako svůj parametr akceptuje generátor (což je většina funkcí) je funkcí vyššího řádu.

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:

# Příklad Stručný popis Cesta
1 01_install_test.lua načtení knihovny Lua Fun a otestování existence generátoru range https://github.com/tisnik/functional-lua/tree/master/lua_fun/01_in­stall_test.lua
2 02_import_everything.lua explicitní přidání všech funkcí do globálního jmenného prostoru https://github.com/tisnik/functional-lua/tree/master/lua_fun/02_im­port_everything.lua
3 03_simpler_install_test.lua též operace, ovšem provedená na jediném řádku https://github.com/tisnik/functional-lua/tree/master/lua_fun/03_sim­pler_install_test.lua
4 04_print_sequence.lua základní práce se sekvencemi https://github.com/tisnik/functional-lua/tree/master/lua_fun/04_prin­t_sequence.lua
5 05_range_generator.lua generátor konečné sekvence range https://github.com/tisnik/functional-lua/tree/master/lua_fun/05_ran­ge_generator.lua
6 06_infinite_sequences_generators.lua vytvoření generátorů nekonečných sekvencí https://github.com/tisnik/functional-lua/tree/master/lua_fun/06_in­finite_sequences_generator­s.lua/
7 07_infinite_sequence_values.lua hodnoty získané z generátorů nekonečných sekvencí https://github.com/tisnik/functional-lua/tree/master/lua_fun/07_in­finite_sequence_values.lua
8 08_generator_state.lua přečtení stavu generátorů https://github.com/tisnik/functional-lua/tree/master/lua_fun/08_ge­nerator_state.lua
9 09_head_tail.lua použití funkcí head a tail https://github.com/tisnik/functional-lua/tree/master/lua_fun/09_he­ad_tail.lua
10 10_nth.lua získání n-tého prvku sekvence funkcí nth https://github.com/tisnik/functional-lua/tree/master/lua_fun/10_nth.lua
11 11_taken_dropn.lua řez sekvencí pomocí funkcí taken a dropn https://github.com/tisnik/functional-lua/tree/master/lua_fun/11_ta­ken_dropn.lua
12 12_take_drop_number.lua funkce take a drop s indexy https://github.com/tisnik/functional-lua/tree/master/lua_fun/12_ta­ke_drop_number.lua
13 13_split_number.lua funkce split rozdělující sekvenci https://github.com/tisnik/functional-lua/tree/master/lua_fun/13_split_num­ber.lua
14 14_take_while_drop_while.lua funkce vyššího řádu take_while a drop_while s predikáty https://github.com/tisnik/functional-lua/tree/master/lua_fun/14_ta­ke_while_drop_while.lua
15 15_take_drop_predicate.lua funkce vyššího řádu take a drop s predikáty https://github.com/tisnik/functional-lua/tree/master/lua_fun/15_ta­ke_drop_predicate.lua
16 16_split_predicate.lua funkce vyššího řádu split s predikáty https://github.com/tisnik/functional-lua/tree/master/lua_fun/16_split_pre­dicate.lua
17 17_zip.lua použití funkce zip https://github.com/tisnik/functional-lua/tree/master/lua_fun/17_zip.lua

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):

CS24_early

  1. Programovací jazyk Lua
    https://www.root.cz/clanky/pro­gramovaci-jazyk-lua/
  2. Základní konstrukce v programovacím jazyku Lua
    https://www.root.cz/clanky/zakladni-konstrukce-v-programovacim-jazyku-lua/
  3. Operátory a asociativní pole v jazyku Lua
    https://www.root.cz/clanky/operatory-a-asociativni-pole-v-jazyku-lua/
  4. Funkce v programovacím jazyku Lua
    https://www.root.cz/clanky/funkce-v-programovacim-jazyku-lua/
  5. Funkce v programovacím jazyku Lua – uzávěry
    https://www.root.cz/clanky/funkce-v-programovacim-jazyku-lua-uzavery/
  6. Programovací jazyk Lua vestavěný do aplikací
    https://www.root.cz/clanky/pro­gramovaci-jazyk-lua-vestaveny-do-aplikaci/
  7. Programovací jazyk Lua v aplikacích II
    https://www.root.cz/clanky/pro­gramovaci-jazyk-lua-v-aplikacich-ii/
  8. Objektově orientované programování v Lua
    https://www.root.cz/clanky/objektove-orientovane-programovani-v-lua/
  9. Objektově orientované programování v Lua II
    https://www.root.cz/clanky/objektove-orientovane-programovani-v-lua-ii/
  10. Programovací jazyk Lua a koprogramy
    https://www.root.cz/clanky/pro­gramovaci-jazyk-lua-a-koprogramy/
  11. Knihovny a frameworky pro programovací jazyk Lua
    https://www.root.cz/clanky/knihovny-a-frameworky-pro-programovaci-jazyk-lua/
  12. Lua + LÖVE: vytvořte si vlastní hru
    https://www.root.cz/clanky/lua-love-vytvorte-si-vlastni-hru/
  13. Hrátky se systémem LÖVE
    https://www.root.cz/clanky/hratky-se-systemem-love/
  14. Vytváříme hru v systému LÖVE
    https://www.root.cz/clanky/vytvarime-hru-v-systemu-love/
  15. Hrátky se systémem LÖVE – částicové systémy
    https://www.root.cz/clanky/hratky-se-systemem-love-casticove-systemy/
  16. 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/
  17. 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/
  18. 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/
  19. Hrátky se systémem LÖVE – dokončení
    https://www.root.cz/clanky/hratky-se-systemem-love-dokonceni/
  20. LuaJ – implementace jazyka Lua v Javě
    https://www.root.cz/clanky/luaj-ndash-implementace-jazyka-lua-v-jave/
  21. LuaJ a skriptování podle specifikace JSR-223
    https://www.root.cz/clanky/luaj-a-skriptovani-podle-specifikace-jsr-223/
  22. Metalua: programovatelné rozšíření sémantiky jazyka Lua
    https://www.root.cz/clanky/metalua-programovatelne-rozsireni-semantiky-jazyka-lua/
  23. Metalua: užitečná rozšíření jazyka Lua
    https://www.root.cz/clanky/metalua-uzitecna-rozsireni-jazyka-lua/
  24. Programovací jazyk Lua v roli skriptovacího jazyka pro WWW stránky
    https://www.root.cz/clanky/pro­gramovaci-jazyk-lua-v-roli-skriptovaciho-jazyka-pro-www-stranky/
  25. 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/
  26. 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/
  27. 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/
  28. Profilery pro programovací jazyk Lua
    https://www.root.cz/clanky/profilery-pro-programovaci-jazyk-lua/
  29. Využití knihovny debug v programovacím jazyku Lua
    https://www.root.cz/clanky/vyuziti-knihovny-debug-v-programovacim-jazyku-lua/
  30. 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ů:

  1. LuaJIT – Just in Time překladač pro programovací jazyk Lua
    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 (2)
    https://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-2/
  3. 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/
  4. 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/
  5. 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/
  6. 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/
  7. 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/
  8. 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/
  9. 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/
  10. 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/
  11. 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/
  12. 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:

  1. Skriptovací jazyk Lua v aplikacích naprogramovaných v Go
    https://www.root.cz/clanky/skriptovaci-jazyk-lua-v-aplikacich-naprogramovanych-v-go/
  2. Programovací jazyk Lua v roli skriptovacího jazyka pro WWW stránky
    https://www.root.cz/clanky/pro­gramovaci-jazyk-lua-v-roli-skriptovaciho-jazyka-pro-www-stranky/
  3. LuaJ – implementace jazyka Lua v Javě
    https://www.root.cz/clanky/luaj-ndash-implementace-jazyka-lua-v-jave/
  4. 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/
Poznámka: předchozí články sice nepokrývají ekosystém tohoto jazyka dokonale, ovšem přibližně 90% všech relevantních informací je uvedeno.

20. Odkazy na Internetu

  1. Repositář projektu Lua Fun
    https://github.com/luafun/luafun
  2. Lua Functional 0.1.3 documentation
    https://luafun.github.io/re­ference.html
  3. Getting Started
    https://luafun.github.io/get­ting_started.html
  4. Rockspec knihovny Fun
    https://raw.githubusercon­tent.com/luafun/luafun/mas­ter/fun-scm-1.rockspec
  5. Awesome Lua – A curated list of quality Lua packages and resources.
    https://github.com/LewisJEllis/awesome-lua
  6. Repositář projektu Moses
    https://github.com/Yonaba/Moses/
  7. Lambda the Ultimate: Coroutines in Lua
    http://lambda-the-ultimate.org/node/438
  8. Coroutines Tutorial
    http://lua-users.org/wiki/CoroutinesTutorial
  9. Lua Coroutines Versus Python Generators
    http://lua-users.org/wiki/LuaCorouti­nesVersusPythonGenerators
  10. Programming in Lua 9.1 – Coroutine Basics
    http://www.lua.org/pil/9.1.html
  11. Wikipedia CZ: Koprogram
    http://cs.wikipedia.org/wi­ki/Koprogram
  12. Wikipedia EN: Coroutine
    http://en.wikipedia.org/wi­ki/Coroutine
  13. Repositář knihovny Moses
    https://github.com/Yonaba/Moses/
  14. Návod k použití knihovny Moses
    https://github.com/Yonaba/Mo­ses/blob/master/doc/tutori­al.md
  15. How to understand clojure's lazy-seq
    https://stackoverflow.com/qu­estions/44095400/how-to-understand-clojures-lazy-seq
  16. Lua Implementations
    http://lua-users.org/wiki/LuaImplementations
  17. Generator (computer programming)
    https://en.wikipedia.org/wi­ki/Generator_(computer_pro­gramming)
  18. Lambda the Ultimate: Coroutines in Lua,
    http://lambda-the-ultimate.org/node/438
  19. Coroutines Tutorial,
    http://lua-users.org/wiki/CoroutinesTutorial
  20. Lua Coroutines Versus Python Generators,
    http://lua-users.org/wiki/LuaCorouti­nesVersusPythonGenerators
  21. Category:Lua na Rosetta code
    https://rosettacode.org/wi­ki/Category:Lua
  22. Programming in Lua: 23 – The Debug Library
    http://www.lua.org/pil/23.html
  23. Programming in Lua: 23.1 – Introspective Facilities
    http://www.lua.org/pil/23.1.html
  24. Programming in Lua: 23.2 – Hooks
    http://www.lua.org/pil/23.2.html
  25. Lua 5.2 Reference Manual: 6.10 – The Debug Library
    http://www.lua.org/manual/5­.2/manual.html#6.10
  26. Turtles all the way down
    https://en.wikipedia.org/wi­ki/Turtles_all_the_way_down
  27. Issues k projektu LuaFun
    https://github.com/luafun/lu­afun/issues
  28. Archived | Embed Lua for scriptable apps
    https://developer.ibm.com/tutorials/l-embed-lua/
  29. Embedding Lua
    https://www.oreilly.com/li­brary/view/lua-quick-start/9781789343229/3a6f3daf-f74c-4a25-a125–584da58568e4.xhtml

Byl pro vás článek přínosný?

Autor článku

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