Hlavní navigace

Podpora funkcionálního programovaní v jazyku Lua s využitím knihovny Moses

Druhou knihovnou určenou pro podporu funkcionálního programování v jazyku Lua je knihovna Moses. Ta se v některých ohledech odlišuje od již popsané knihovny Lua Fun a volba záleží na požadavcích konkrétního projektu.
Pavel Tišnovský 28. 7. 2020
Doba čtení: 35 minut

Sdílet

11. Operace shift

12. Operace pop

13. Řez polem s využitím operace slice

14. Získání unikátních prvků v poli

15. Výběr prvků ze začátku pole funkcí vyššího řádu selectWhile

16. Přeskočení prvků ze začátku pole funkcí vyššího řádu dropWhile

17. Výběr prvků z pole s využitím funkce vyššího řádu filter

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. Podpora funkcionálního programovaní v jazyku Lua s využitím knihovny Moses

Na předchozí dva články [1] [2], v nichž jsme si ukazovali některé možnosti nabízené knihovnou Lua Fun v oblasti funkcionálního programování a zpracování konečných i nekonečných sekvencí, dnes navážeme. Popíšeme si totiž knihovnu nazvanou Moses, jejíž cíl je přibližně stejný – umožnit, aby se v programovacím jazyku Lua používaly programové konstrukce vycházející z funkcionálního programování: zpracování sekvencí (typicky bez modifikace původní sekvence), použití funkcí vyššího řádu apod. Zatímco knihovna Lua Fun byla navržena s ohledem na dosažení co nejlepšího výpočetního výkonu při jejím spuštění v LuaJITu, je zaměření knihovny Moses spíše obecnější, takže výpočty a operace v ní provedené mohou být pomalejší. Ostatně knihovna Moses je větší i co do objemu programového kódu, protože její čitelná verze má velikost přibližně 91 kB a minifikovaná verze 32 kB, zatímco čitelná verze knihovny Lua Fun má velikost 28 kB (bez minifikace).

Navíc je již z prvního porovnání obou knihoven patrné, že Lua Fun je založena na iterátorech a generátorech konečných či nekonečných sekvencí, zatímco Moses lze chápat spíše jako sadu užitečných operací pro zpracování tabulek, polí (což bychom spíš měli překládat jako seznamy) a objektů. Navíc zde ovšem nalezneme i funkci memoize, jíž lze použít pro zapamatování předchozích výsledků volání nějaké funkce (která by pochopitelně měla být referenčně transparentní). Proto bude popis knihovny Moses organizován poněkud jiným způsobem, než tomu bylo u knihovny Lua Fun. V knihovně Moses musíme především rozlišovat operace určené pro práci s poli (array), které jsou odlišné od funkcí pro práci s tabulkami (table). Použitá terminologie ovšem může být matoucí, protože array zde znamená seznam (list) a pro některé operace množinu (set), kdežto tabulka znamená slovník (dictionary):

# Termín použitý v Moses Běžný význam
1 array list, set
2 table dictionary
Poznámka: sice jsme si tedy řekli, že základní zaměření obou knihoven je přibližně stejné, ale implementovaná funkcionalita i sémantika se odlišuje. Záleží tedy na konkrétním projektu a preferencích samotného vývojáře, kterou knihovnu si pro svoji aplikaci zvolí. Pokud preferujete pohled na data formou konečných a nekonečných sekvencí (tedy „styl Clojure“), je ideální použít Lua Fun, pokud naopak plánujete práci s klasickými tabulkami jazyka Lua, je výhodnější použít knihovnu Moses.

2. Instalace knihovny Moses a první testy

Většinu „funkcionálních“ pojmů, s nimiž se v dnešním článku setkáme, jsme si již popsali v úvodním článku, takže se dnes již můžeme zaměřit na ukázání způsobů použití této knihovny. Nejdříve ji pochopitelně musíme získat, což je ve skutečnosti velmi snadné. Postačuje použít Git pro naklonování repositáře projektu:

$ git clone git://github.com/Yonaba/Moses.git
 
Cloning into 'Moses'...
remote: Enumerating objects: 2017, done.
remote: Total 2017 (delta 0), reused 0 (delta 0), pack-reused 2017
Receiving objects: 100% (2017/2017), 1.41 MiB | 1.79 MiB/s, done.
Resolving deltas: 100% (1180/1180), done.

V naklonovaném repositáři se nachází zdrojové soubory této knihovny uložené v souboru se jménem moses.lua, popř. minifikovaná varianta pojmenovaná moses_min.lua. Jeden z těchto souborů postačuje přidat buď přímo ke zdrojovým kódům aplikace nebo do adresáře, na který ukazuje proměnná prostředí LUAPATH.

Poznámka: knihovna Moses je, podobně jako samotná Lua, vydaná pod MIT licencí, takže je zařazení zdrojových textů knihovny Moses do aplikací většinou možné.

Pochopitelně je možné pro instalaci použít i správce balíčků LuaRocks.

Po instalaci je možné otestovat, zda je možné knihovnu Moses načíst do skriptu a použít funkce, které jsou v ní definovány:

-- načtení knihovny Moses
local M = require "moses"
 
 
-- pokus o použití funkce z knihovny Moses
table = M.range(10, 1)
for index, value in ipairs(table) do
    print(index, value)
end

Výsledný skript by měl být spustitelný (resp. přesněji řečeno interpretovatelný):

$ lua 01_install_test.lua
 
1       10
2       9
3       8
4       7
5       6
6       5
7       4
8       3
9       2
10      1

3. Import funkcí poskytovaných knihovnou Moses v rámci zvoleného kontextu

Existuje i druhá varianta importů funkcí, které jsou poskytované knihovnou Moses. Tato varianta je založena na tom, že se všechny funkce uloží buď do globálního jmenného prostoru (což je pro menší projekty užitečná vlastnost) nebo do zvoleného kontextu. Nejdříve si ukažme import funkcí do globálního jmenného prostoru. Pro tento účel se použije funkce import, které se nepředají žádné parametry:

-- načtení knihovny Moses a současně import symbolů do globálního jmenného prostoru
M = require "moses"
M.import()
 
 
-- pokus o použití funkce z knihovny Moses
table = range(10, 1)
for index, value in ipairs(table) do
    print(index, value)
end

I tento skript by měl být interpretrem bez problémů spustitelný:

$ lua 02_import_everything.lua
 
1       10
2       9
3       8
4       7
5       6
6       5
7       4
8       3
9       2
10      1

Alternativně lze využít import funkcí do takzvaného kontextu, což může být libovolná (i prázdná) tabulka. K importovaným funkcím se bude přistupovat přes tečkovou notaci popř. výběrem přes klíč (což ovšem málokdo bude zapisovat). V dalším příkladu jsou vytvořeny a využity dva kontexty:

-- vytvoření dvou kontextů
context1 = {}
context2 = {}
 
-- načtení knihovny Moses
M = require "moses"
 
-- import funkcí do kontextů
M.import(context1)
M.import(context2)
 
 
-- pokus o použití funkce z knihovny Moses
print("M")
table = M.range(10, 1)
for index, value in ipairs(table) do
    print(index, value)
end
 
print()
 
-- nyní vyzkoušíme stejnou funkci, ovšem v prvním kontextu
print("context 1")
table = context1.range(10, 1)
for index, value in ipairs(table) do
    print(index, value)
end
 
print()
 
-- nyní vyzkoušíme stejnou funkci, ovšem ve druhém kontextu
print("context 2")
table = context2.range(10, 1)
for index, value in ipairs(table) do
    print(index, value)
end

Výsledek po spuštění příkladu:

$ lua 03_import_into_context.lua
 
M
1       10
2       9
3       8
4       7
5       6
6       5
7       4
8       3
9       2
10      1
 
context 1
1       10
2       9
3       8
4       7
5       6
6       5
7       4
8       3
9       2
10      1
 
context 2
1       10
2       9
3       8
4       7
5       6
6       5
7       4
8       3
9       2
10      1

4. Funkce pro vytvoření sekvencí (konstruktory polí)

Podobně jako se v knihovně Lua Fun nacházelo několik funkcí určených pro konstrukci konečných i nekonečných sekvencí, nalezneme v knihovně Moses funkce pro konstrukci (konečných) polí – většina těchto funkcí tedy potřebuje znát i informaci o délce nově konstruovaného pole. Jedná se o následující konstruktory:

# Funkce Význam
1 zeros() pole obsahující nulové prvky
2 ones() pole obsahující prvky s hodnotou 1
3 rep() pole obsahující prvky se shodnou hodnotou předanou do konstruktoru
4 vector() implementuje stejnou funkcionalitu, jako konstruktor rep
5 range() pole s aritmetickou posloupností zadané délky

První konstruktor se jmenuje zeros() a slouží pro vytvoření pole obsahujícího samé nuly (tedy N nulových prvků). Použití tohoto konstruktoru je snadné:

-- načtení knihovny Moses a současně import symbolů do globálního jmenného prostoru
M = require "moses"
M.import()
 
 
-- tisk obsahu pole
function printArray(array)
    for index, value in ipairs(array) do
        print(index, value)
    end
end
 
 
-- oddělení obsahu
function printSeparator()
    print("-------------------------------")
end
 
-- pole s nulami
a1 = zeros(10)
printSeparator()
print("zeros(10)")
printArray(a1)
 
-- pole s jedním prvkem
a2 = zeros(1)
printSeparator()
print("zeros(1)")
printArray(a2)
 
-- prázdné pole s nulami
a3 = zeros(0)
printSeparator()
print("zeros(0)")
printArray(a3)
 
-- pole s nulami
a4 = zeros(-1)
printSeparator()
print("zeros(-1)")
printArray(a4)

Výsledky zobrazené po spuštění tohoto demonstračního příkladu:

-------------------------------
zeros(10)
1       0
2       0
3       0
4       0
5       0
6       0
7       0
8       0
9       0
10      0
-------------------------------
zeros(1)
1       0
-------------------------------
zeros(0)
-------------------------------
zeros(-1)
Poznámka: v posledních dvou případech se zkonstruovalo prázdné pole.

Druhý konstruktor se jmenuje ones a slouží pro konstrukci pole obsahujícího pouze prvky s hodnotou 1:

-- načtení knihovny Moses a současně import symbolů do globálního jmenného prostoru
M = require "moses"
M.import()
 
 
-- tisk obsahu pole
function printArray(array)
    for index, value in ipairs(array) do
        print(index, value)
    end
end
 
 
-- oddělení obsahu
function printSeparator()
    print("-------------------------------")
end
 
-- pole s jedničkami
a1 = ones(10)
printSeparator()
print("ones(10)")
printArray(a1)
 
-- pole s jedním prvkem
a2 = ones(1)
printSeparator()
print("ones(1)")
printArray(a2)
 
-- prázdné pole s jedničkami
a3 = ones(0)
printSeparator()
print("ones(0)")
printArray(a3)
 
-- pole s jedničkami
a4 = ones(-1)
printSeparator()
print("ones(-1)")
printArray(a4)

Opět si ukažme zprávy zobrazené po spuštění tohoto demonstračního příkladu:

-------------------------------
ones(10)
1       1
2       1
3       1
4       1
5       1
6       1
7       1
8       1
9       1
10      1
-------------------------------
ones(1)
1       1
-------------------------------
ones(0)
-------------------------------
ones(-1)

Třetí konstruktor se jmenuje rep a slouží pro vytvoření pole se stejnými prvky. Tentokrát jsou ovšem prvky specifikované programátorem při volání konstruktoru. Ostatní chování je shodné s konstruktory zeros a ones:

-- načtení knihovny Moses a současně import symbolů do globálního jmenného prostoru
M = require "moses"
M.import()
 
 
-- tisk obsahu pole
function printArray(array)
    for index, value in ipairs(array) do
        print(index, value)
    end
end
 
 
-- oddělení obsahu
function printSeparator()
    print("-------------------------------")
end
 
-- pole s opakující se hodnotou
a1 = rep("*", 10)
printSeparator()
print("rep('*', 10)")
printArray(a1)
 
-- pole s jedinou hodnotou
a2 = rep("*", 1)
printSeparator()
print("rep('*', 1)")
printArray(a2)
 
-- prázdné pole s opakující se hodnotou
a3 = rep("*", 0)
printSeparator()
print("rep('*', 0)")
printArray(a3)
 
-- pole s opakující se hodnotou
a4 = rep("*", -1)
printSeparator()
print("rep('*', -1)")
printArray(a5)
Poznámka: v tomto příkladu naznačujeme, že prvky polí nemusí být pouze numerické hodnoty. Použít lze hodnotu libovolného typu, a to včetně speciální hodnoty nil.

Výsledky:

-------------------------------
rep('*', 10)
1       *
2       *
3       *
4       *
5       *
6       *
7       *
8       *
9       *
10      *
-------------------------------
rep('*', 1)
1       *
-------------------------------
rep('*', 0)
-------------------------------
rep('*', -1)

Pole se stejnými vlastnostmi lze zkonstruovat i s využitím funkce vector, což opět vnáší určitý zmatek do použité terminologie (vektor má v jiných jazycích a knihovnách dosti odlišný význam). Z tohoto důvodu bude asi lepší použít výše zmíněný konstruktor rep:

-- načtení knihovny Moses a současně import symbolů do globálního jmenného prostoru
M = require "moses"
M.import()
 
 
-- tisk obsahu pole
function printArray(array)
    for index, value in ipairs(array) do
        print(index, value)
    end
end
 
 
-- oddělení obsahu
function printSeparator()
    print("-------------------------------")
end
 
-- pole s opakující se hodnotou
a1 = vector("*", 10)
printSeparator()
print("vector('*', 10)")
printArray(a1)
 
-- pole s jedinou hodnotou
a2 = vector("*", 1)
printSeparator()
print("vector('*', 1)")
printArray(a2)
 
-- prázdné pole s opakující se hodnotou
a3 = vector("*", 0)
printSeparator()
print("vector('*', 0)")
printArray(a3)
 
-- pole s opakující se hodnotou
a4 = vector("*", -1)
printSeparator()
print("vector('*', -1)")
printArray(a5)

Výsledky po spuštění:

-------------------------------
vector('*', 10)
1       *
2       *
3       *
4       *
5       *
6       *
7       *
8       *
9       *
10      *
-------------------------------
vector('*', 1)
1       *
-------------------------------
vector('*', 0)
-------------------------------
vector('*', -1)

5. Konstruktor pole range

I v knihovně Moses pochopitelně nalezneme konstruktor range, který se ovšem některými svými vlastnostmi odlišuje od generátoru range z Pythonu či z knihovny Lua Fun. Konstruktor range akceptuje buď pouze zadání konečné hodnoty (potom se počítá od jedničky, jak je v jazyce Lua zvykem); lze ovšem zadat i počáteční hodnotu a navíc i krok. V případě, že je počáteční hodnota větší než hodnota koncová, bude krok automaticky nastaven na hodnotu –1, pochopitelně pokud není uveden explicitně. Navíc mohou být obě meze i krok specifikovány neceločíselnou hodnotou. Opět se podívejme na několik příkladů použití tohoto velmi užitečného konstruktoru:

-- načtení knihovny Moses a současně import symbolů do globálního jmenného prostoru
M = require "moses"
M.import()
 
 
-- tisk obsahu pole
function printArray(array)
    for index, value in ipairs(array) do
        print(index, value)
    end
end
 
 
-- oddělení obsahu
function printSeparator()
    print("-------------------------------")
end
 
 
-- prázdné pole při neuvedení rozsahu
a0 = range()
printSeparator()
print("range()")
printArray(a0)
 
 
-- první pole začínající standardně od jedničky
a1 = range(10)
printSeparator()
print("range(10)")
printArray(a1)
 
 
-- druhé pole s explicitní specifikací hodnoty prvního prvku
a2 = range(1, 10)
printSeparator()
print("range(1, 10)")
printArray(a2)
 
 
-- třetí pole se specifikací kroku (rozdílu mezi dvěma prvky)
a3 = range(1, 10, 2)
printSeparator()
print("range(1, 10, 2)")
printArray(a3)
 
 
-- čtvrté pole s prvky počítanými pozpátku
a4 = range(10, 1)
printSeparator()
print("range(10, 1)")
printArray(a4)
 
 
-- páté pole počítané pozpátku s kladným krokem
a5 = range(10, 1, 2)
printSeparator()
print("range(10, 1, 2)")
printArray(a5)
 
 
-- šesté pole počítané pozpátku se záporným krokem
a6 = range(10, 1, -2)
printSeparator()
print("range(10, 1, -2)")
printArray(a6)
 
 
-- sedmé pole s neceločíselným krokem
a7 = range(1, 5, 0.5)
printSeparator()
print("range(10, 5, 0.5)")
printArray(a7)
 
 
-- osmé pole s neceločíselným krokem a počáteční hodnotou typu double
a8 = range(1.0, 5, 0.5)
printSeparator()
print("range(10, 5, 0.5)")
printArray(a8)
 
 
-- deváté pole testující vliv problematické hodnoty 0.1
a9 = range(0, 1, 0.1)
printSeparator()
print("range(0, 1, 0.1)")
printArray(a9)
 
 
-- desáté pole s počáteční hodnotou typu double
a10 = range(0.0, 1, 0.1)
printSeparator()
print("range(0.0, 1, 0.1)")
printArray(a9)

S výsledky:

-------------------------------
range()
-------------------------------
range(10)
1       1
2       2
3       3
4       4
5       5
6       6
7       7
8       8
9       9
10      10
-------------------------------
range(1, 10)
1       1
2       2
3       3
4       4
5       5
6       6
7       7
8       8
9       9
10      10
-------------------------------
range(1, 10, 2)
1       1
2       3
3       5
4       7
5       9
-------------------------------
range(10, 1)
1       10
2       9
3       8
4       7
5       6
6       5
7       4
8       3
9       2
10      1
-------------------------------
range(10, 1, 2)
1       10
-------------------------------
range(10, 1, -2)
1       10
2       8
3       6
4       4
5       2
-------------------------------
range(10, 5, 0.5)
1       1
2       1.5
3       2.0
4       2.5
5       3.0
6       3.5
7       4.0
8       4.5
9       5.0
-------------------------------
range(10, 5, 0.5)
1       1.0
2       1.5
3       2.0
4       2.5
5       3.0
6       3.5
7       4.0
8       4.5
9       5.0
-------------------------------
range(0, 1, 0.1)
1       0
2       0.1
3       0.2
4       0.3
5       0.4
6       0.5
7       0.6
8       0.7
9       0.8
10      0.9
11      1.0
-------------------------------
range(0.0, 1, 0.1)
1       0
2       0.1
3       0.2
4       0.3
5       0.4
6       0.5
7       0.6
8       0.7
9       0.8
10      0.9
11      1.0

6. Získání otočeného pole

Tato kapitola bude velmi stručná, protože se v ní zmíníme o jediné funkci nazvané reverse. Tato funkce, jak již její název naznačuje, slouží pro získání nového pole, které obsahuje stejné prvky jako pole původní, ovšem uložené v opačném pořadí.

Poznámka: v knihovně Lua Fun orientované na sekvence (a to i sekvence nekonečné) tuto funkci nenajdeme a vlastně v ní nenajdeme žádnou funkci, která by s prvky sekvencí pracovala jinak než v přirozeném pořadí.

Podívejme se na příklad použití funkce reverse, a to na zdrojovém kódu, který vznikl nepatrnou úpravou předchozího demonstračního příkladu:

-- načtení knihovny Moses a současně import symbolů do globálního jmenného prostoru
M = require "moses"
M.import()
 
 
-- tisk obsahu pole
function printArray(array)
    for index, value in ipairs(array) do
        print(index, value)
    end
end
 
 
-- oddělení obsahu
function printSeparator()
    print("-------------------------------")
end
 
 
-- prázdné pole při neuvedení rozsahu
a0 = reverse(range())
printSeparator()
print("reverse(range())")
printArray(a0)
 
 
-- první pole začínající standardně od jedničky
a1 = reverse(range(10))
printSeparator()
print("reverse(range(10))")
printArray(a1)
 
 
-- druhé pole s explicitní specifikací hodnoty prvního prvku
a2 = reverse(range(1, 10))
printSeparator()
print("reverse(range(1, 10))")
printArray(a2)
 
 
-- třetí pole se specifikací kroku (rozdílu mezi dvěma prvky)
a3 = reverse(range(1, 10, 2))
printSeparator()
print("reverse(range(1, 10, 2))")
printArray(a3)
 
 
-- čtvrté pole s prvky počítanými pozpátku
a4 = reverse(range(10, 1))
printSeparator()
print("reverse(range(10, 1))")
printArray(a4)
 
 
-- páté pole počítané pozpátku s kladným krokem
a5 = reverse(range(10, 1, 2))
printSeparator()
print("reverse(range(10, 1, 2))")
printArray(a5)
 
 
-- šesté pole počítané pozpátku se záporným krokem
a6 = reverse(range(10, 1, -2))
printSeparator()
print("reverse(range(10, 1, -2))")
printArray(a6)
 
 
-- sedmé pole s neceločíselným krokem
a7 = reverse(range(1, 5, 0.5))
printSeparator()
print("reverse(range(10, 5, 0.5))")
printArray(a7)
 
 
-- osmé pole s neceločíselným krokem a počáteční hodnotou typu double
a8 = reverse(range(1.0, 5, 0.5))
printSeparator()
print("reverse(range(10, 5, 0.5))")
printArray(a8)
 
 
-- deváté pole testující vliv problematické hodnoty 0.1
a9 = reverse(range(0, 1, 0.1))
printSeparator()
print("reverse(range(0, 1, 0.1))")
printArray(a9)
 
 
-- desáté pole s počáteční hodnotou typu double
a10 = reverse(range(0.0, 1, 0.1))
printSeparator()
print("reverse(range(0.0, 1, 0.1))")
printArray(a9)

Zprávy získané po spuštění tohoto příkladu:

-------------------------------
reverse(range())
-------------------------------
reverse(range(10))
1       10
2       9
3       8
4       7
5       6
6       5
7       4
8       3
9       2
10      1
-------------------------------
reverse(range(1, 10))
1       10
2       9
3       8
4       7
5       6
6       5
7       4
8       3
9       2
10      1
-------------------------------
reverse(range(1, 10, 2))
1       9
2       7
3       5
4       3
5       1
-------------------------------
reverse(range(10, 1))
1       1
2       2
3       3
4       4
5       5
6       6
7       7
8       8
9       9
10      10
-------------------------------
reverse(range(10, 1, 2))
1       10
-------------------------------
reverse(range(10, 1, -2))
1       2
2       4
3       6
4       8
5       10
-------------------------------
reverse(range(10, 5, 0.5))
1       5.0
2       4.5
3       4.0
4       3.5
5       3.0
6       2.5
7       2.0
8       1.5
9       1
-------------------------------
reverse(range(10, 5, 0.5))
1       5.0
2       4.5
3       4.0
4       3.5
5       3.0
6       2.5
7       2.0
8       1.5
9       1.0
-------------------------------
reverse(range(0, 1, 0.1))
1       1.0
2       0.9
3       0.8
4       0.7
5       0.6
6       0.5
7       0.4
8       0.3
9       0.2
10      0.1
11      0
-------------------------------
reverse(range(0.0, 1, 0.1))
1       1.0
2       0.9
3       0.8
4       0.7
5       0.6
6       0.5
7       0.4
8       0.3
9       0.2
10      0.1
11      0
Poznámka: povšimněte si, že je možné otočit i prázdné pole, takže není nutné v programem tuto možnost explicitně ošetřovat.

7. Získání n prvních popř. n posledních prvků z pole

Při zpracování seznamů či sekvencí se často používají funkce nazvané first a rest. U polí je situace jednodušší, neboť je možné přistupovat k prvkům náhodně, je známá délka pole atd. Proto knihovna Moses nabízí následující funkce, které dokážou z pole získat prvky ze začátku nebo naopak z konce pole:

# Funkce Stručný popis
1 first získání prvních N prvků z pole
2 last získání posledních N prvků z pole
     
3 initial získání prvků z pole kromě posledních N prvků
4 rest získání všech prvků z pole za indexem N
Poznámka: jméno poslední funkce je opět matoucí, zejména pro ty vývojáře, kteří používají i jiné programovací jazyky popř. knihovny.

Podívejme se nejprve na chování funkce first, která získá buď první prvek z pole (pokud N není zadáno) nebo prvních N prvků. Druhá část příkladu ukazuje použití funkce last:

-- načtení knihovny Moses a současně import symbolů do globálního jmenného prostoru
M = require "moses"
M.import()
 
 
-- tisk obsahu pole
function printArray(array)
    for index, value in ipairs(array) do
        print(index, value)
    end
end
 
 
-- oddělení obsahu
function printSeparator()
    print("-------------------------------")
end
 
 
-- vstupní pole
s = range(12)
printSeparator()
print("original array")
printArray(s)
 
 
-- funkce first
printSeparator()
print("first(s)")
printArray(first(s))
 
 
-- funkce first s udáním počtu prvků
printSeparator()
print("first(s, 5)")
printArray(first(s, 5))
 
 
-- funkce first s udáním nulového počtu prvků
printSeparator()
print("first(s, 0)")
printArray(first(s, 0))
 
 
-- funkce last
printSeparator()
print("last(s)")
printArray(last(s))
 
 
-- funkce last s udáním počtu prvků
printSeparator()
print("last(s, 5)")
printArray(last(s, 5))
 
 
-- funkce last s udáním nulového počtu prvků
printSeparator()
print("last(s, 0)")
printArray(last(s, 0))

Výsledky:

-------------------------------
original array
1       1
2       2
3       3
4       4
5       5
6       6
7       7
8       8
9       9
10      10
11      11
12      12
-------------------------------
first(s)
1       1
-------------------------------
first(s, 5)
1       1
2       2
3       3
4       4
5       5
-------------------------------
first(s, 0)
-------------------------------
last(s)
1       2
2       3
3       4
4       5
5       6
6       7
7       8
8       9
9       10
10      11
11      12
-------------------------------
last(s, 5)
1       8
2       9
3       10
4       11
5       12
-------------------------------
last(s, 0)
Poznámka: povšimněte si, že lze vrátit i prázdné pole.

8. Získání části pole funkcemi initialrest

Další dvě funkce zmíněné již v rámci předchozí kapitoly umožňují, aby se při přístupu k části pole nemusely používat (relativně složité) výpočty zahrnující délku pole (s běžnými problémy typu ±1). Operace prováděné těmito funkcemi jsou založeny na first a last, ovšem neuvádí se počet prvků, které se mají přečíst, ale relativní index od druhého konce pole. Opět si ukažme, jak se tyto operace používají:

-- načtení knihovny Moses a současně import symbolů do globálního jmenného prostoru
M = require "moses"
M.import()
 
 
-- tisk obsahu pole
function printArray(array)
    for index, value in ipairs(array) do
        print(index, value)
    end
end
 
 
-- oddělení obsahu
function printSeparator()
    print("-------------------------------")
end
 
 
-- vstupní pole
s = range(12)
printSeparator()
print("original array")
printArray(s)
 
 
-- funkce initial
printSeparator()
print("initial(s)")
printArray(initial(s))
 
 
-- funkce initial s udáním počtu prvků
printSeparator()
print("initial(s, 5)")
printArray(initial(s, 5))
 
 
-- funkce initial s udáním nulového počtu prvků
printSeparator()
print("initial(s, 0)")
printArray(initial(s, 0))
 
 
-- funkce rest
printSeparator()
print("rest(s)")
printArray(rest(s))
 
 
-- funkce rest s udáním počtu prvků
printSeparator()
print("rest(s, 5)")
printArray(rest(s, 5))
 
 
-- funkce rest s udáním nulového počtu prvků
printSeparator()
print("rest(s, 0)")
printArray(rest(s, 0))

Ze zobrazených výsledků je patrné rozdílné chování oproti funkcím first a last:

-------------------------------
original array
1       1
2       2
3       3
4       4
5       5
6       6
7       7
8       8
9       9
10      10
11      11
12      12
-------------------------------
initial(s)
1       1
2       2
3       3
4       4
5       5
6       6
7       7
8       8
9       9
10      10
11      11
-------------------------------
initial(s, 5)
1       1
2       2
3       3
4       4
5       5
6       6
7       7
-------------------------------
initial(s, 0)
1       1
2       2
3       3
4       4
5       5
6       6
7       7
8       8
9       9
10      10
11      11
12      12
-------------------------------
rest(s)
1       1
2       2
3       3
4       4
5       5
6       6
7       7
8       8
9       9
10      10
11      11
12      12
-------------------------------
rest(s, 5)
1       5
2       6
3       7
4       8
5       9
6       10
7       11
8       12
-------------------------------
rest(s, 0)
1       1
2       2
3       3
4       4
5       5
6       6
7       7
8       8
9       9
10      10
11      11
12      12
Poznámka: povšimněte si, že initialrest zavolané s nulovým offsetem vrátí původní pole.

9. Pole využité jako množiny, množinové operace

Pole, speciálně pole s unikátními prvky, je možné využít i ve funkci množiny (množiny totiž nejsou v programovacím jazyce Lua základním datovým typem, na rozdíl od například Pythonu). Pro práci s množinami nabízí knihovna Moses několik funkcí:

# Funkce Význam
1 union sjednocení dvou či více množin
2 intersection průnik dvou či více množin
3 difference rozdíl množin
4 symmetricDifference symetrická diference (negace průniku)

Následuje ukázka použití všech čtyř zmíněných množinových operací:

-- načtení knihovny Moses a současně import symbolů do globálního jmenného prostoru
M = require "moses"
M.import()
 
 
-- tisk obsahu pole
function printArrayInLine(array)
    for i, value in ipairs(array) do
        io.write(value)
        if i ~= #array then
            io.write(", ")
        end
    end
    print()
end
 
 
-- oddělení obsahu
function printSeparator()
    print("-------------------------------")
end
 
 
a1 = {1, 2, 3, 4}
a2 = {3, 4, 5, 6}
 
printSeparator()
print("a1")
printArrayInLine(a1)
 
printSeparator()
print("a2")
printArrayInLine(a2)
 
printSeparator()
print("union(a1, a2)")
printArrayInLine(union(a1, a2))
 
printSeparator()
print("intersection(a1, a2)")
printArrayInLine(intersection(a1, a2))
 
printSeparator()
print("difference(a1, a2)")
printArrayInLine(difference(a1, a2))
 
printSeparator()
print("difference(a2, a1)")
printArrayInLine(difference(a2, a1))
 
printSeparator()
print("symmetricDifference(a1, a2)")
printArrayInLine(symmetricDifference(a1, a2))
 
printSeparator()
print("symmetricDifference(a2, a1)")
printArrayInLine(symmetricDifference(a2, a1))

Na výsledcích je vliv všech čtyř operací dobře patrný:

-------------------------------
a1
1, 2, 3, 4
-------------------------------
a2
3, 4, 5, 6
-------------------------------
union(a1, a2)
1, 2, 3, 4, 5, 6
-------------------------------
intersection(a1, a2)
3, 4
-------------------------------
difference(a1, a2)
1, 2
-------------------------------
difference(a2, a1)
5, 6
-------------------------------
symmetricDifference(a1, a2)
1, 2, 5, 6
-------------------------------
symmetricDifference(a2, a1)
5, 6, 1, 2

10. Operace push a unshift

Další dvě funkce, které si dnes ve stručnosti popíšeme, se jmenují push a unshift. První funkce dokáže za konec pole připojit novou hodnotu. Dojde přitom k modifikaci původního pole – nejedná se tedy o operaci, kterou bychom v čistě funkcionální knihovně očekávali. Opakem funkce push je funkce nazvaná unshift, která naopak z pole poslední prvek odstraní a vrátí ho jako svoji návratovou hodnotu:

-- načtení knihovny Moses a současně import symbolů do globálního jmenného prostoru
M = require "moses"
M.import()
 
 
-- tisk obsahu pole
function printArrayInLine(array)
    for i, value in ipairs(array) do
        io.write(value)
        if i ~= #array then
            io.write(", ")
        end
    end
    print()
end
 
 
-- oddělení obsahu
function printSeparator()
    print("-------------------------------")
end
 
 
a = {}
printSeparator()
print("original array")
printArrayInLine(a)
 
printSeparator()
print("after push('a')")
push(a, "a")
printArrayInLine(a)
 
printSeparator()
print("after push('b')")
push(a, "b")
printArrayInLine(a)
 
printSeparator()
print("after push('c')")
push(a, "c")
printArrayInLine(a)
 
for i = 1, 5 do
    printSeparator()
    item = unshift(a)
    print("unshifted item:", item)
    print("after unshift()")
    printArrayInLine(a)
end

Po spuštění tohoto příkladu je patrné, že skutečně dochází k modifikaci původního pole:

-------------------------------
original array
 
-------------------------------
after push('a')
a
-------------------------------
after push('b')
a, b
-------------------------------
after push('c')
a, b, c
-------------------------------
unshifted item: c
after unshift()
a, b
-------------------------------
unshifted item: b
after unshift()
a
-------------------------------
unshifted item: a
after unshift()
 
-------------------------------
unshifted item: nil
after unshift()
 
-------------------------------
unshifted item: nil
after unshift()
 
Poznámka: v posledních třech krocích je vždy vráceno prázdné pole.

11. Operace shift

Zatímco funkce unshift odstraňuje a vrací poslední prvek pole, provádí funkce shift odstranění a vrácení prvku prvního (což teoreticky znamená lineární časovou složitost). Opět si ukažme, jak se tato funkce může použít. Pole nejprve naplníme pomocí push a následně ho postupně vyprázdníme funkcí shift:

-- načtení knihovny Moses a současně import symbolů do globálního jmenného prostoru
M = require "moses"
M.import()
 
 
-- tisk obsahu pole
function printArrayInLine(array)
    for i, value in ipairs(array) do
        io.write(value)
        if i ~= #array then
            io.write(", ")
        end
    end
    print()
end
 
 
-- oddělení obsahu
function printSeparator()
    print("-------------------------------")
end
 
 
a = {}
printSeparator()
print("original array")
printArrayInLine(a)
 
printSeparator()
print("after push('a')")
push(a, "a")
printArrayInLine(a)
 
printSeparator()
print("after push('b')")
push(a, "b")
printArrayInLine(a)
 
printSeparator()
print("after push('c')")
push(a, "c")
printArrayInLine(a)
 
for i = 1, 5 do
    printSeparator()
    item = shift(a)
    print("shifted item:", item)
    print("after shift()")
    printArrayInLine(a)
end

Výsledek po spuštění demonstračního příkladu:

-------------------------------
original array
 
-------------------------------
after push('a')
a
-------------------------------
after push('b')
a, b
-------------------------------
after push('c')
a, b, c
-------------------------------
shifted item:   a
after shift()
b, c
-------------------------------
shifted item:   b
after shift()
c
-------------------------------
shifted item:   c
after shift()
 
-------------------------------
shifted item:   nil
after shift()
 
-------------------------------
shifted item:   nil
after shift()
Poznámka: v posledních třech krocích je opět vždy vráceno prázdné pole.

12. Operace pop

Operace nazvaná pop je pouze jmenným aliasem k výše popsané funkci shift, takže jen pro úplnost:

-- načtení knihovny Moses a současně import symbolů do globálního jmenného prostoru
M = require "moses"
M.import()
 
 
-- tisk obsahu pole
function printArrayInLine(array)
    for i, value in ipairs(array) do
        io.write(value)
        if i ~= #array then
            io.write(", ")
        end
    end
    print()
end
 
 
-- oddělení obsahu
function printSeparator()
    print("-------------------------------")
end
 
 
a = {}
printSeparator()
print("original array")
printArrayInLine(a)
 
printSeparator()
print("after push('a')")
push(a, "a")
printArrayInLine(a)
 
printSeparator()
print("after push('b')")
push(a, "b")
printArrayInLine(a)
 
printSeparator()
print("after push('c')")
push(a, "c")
printArrayInLine(a)
 
for i = 1, 5 do
    printSeparator()
    item = pop(a)
    print("poped item:", item)
    print("after pop()")
    printArrayInLine(a)
end

Výsledky by měly být stejné jako v předchozím demonstračním příkladu:

-------------------------------
original array
 
-------------------------------
after push('a')
a
-------------------------------
after push('b')
a, b
-------------------------------
after push('c')
a, b, c
-------------------------------
poped item:     a
after pop()
b, c
-------------------------------
poped item:     b
after pop()
c
-------------------------------
poped item:     c
after pop()
 
-------------------------------
poped item:     nil
after pop()
 
-------------------------------
poped item:     nil
after pop()

13. Řez polem s využitím operace slice

Podobně jako range, first a last patří do běžného repertoáru knihoven pro práci se sekvencemi popř. s poli (seznamy) i funkce nazvaná slice. Ta provádí řez polem, tj. vrací prvky ležící mezi dvojicí indexů. Jednotlivé implementace se od sebe odlišují podle toho, zda je horní index chápán „včetně“ či „kromě“. V knihovně Moses jsou vráceny všechny prvky od prvního zadaného indexu do indexu druhého, a to včetně obou mezí.

Poznámka: samozřejmě v jazycích, které slice provádí s využitím speciální konstrukce nebo operátoru (Python) není nutné tuto funkci explicitně implementovat. To však není případ programovacího jazyka Lua.

Ukázka použití operace slice:

-- načtení knihovny Moses a současně import symbolů do globálního jmenného prostoru
M = require "moses"
M.import()
 
 
-- tisk obsahu pole
function printArrayInLine(array)
    for i, value in ipairs(array) do
        io.write(value)
        if i ~= #array then
            io.write(", ")
        end
    end
    print()
end
 
 
-- oddělení obsahu
function printSeparator()
    print("-------------------------------")
end
 
 
a = range(10)
 
printSeparator()
print("Original array")
printArrayInLine(a)
 
printSeparator()
print("slice(a)")
printArrayInLine(slice(a))
 
printSeparator()
print("slice(a, 5)")
printArrayInLine(slice(a, 5))
 
printSeparator()
print("slice(a, 5, 5)")
printArrayInLine(slice(a, 5, 5))
 
printSeparator()
print("slice(a, 4, 8)")
printArrayInLine(slice(a, 4, 8))
 
printSeparator()
print("slice(a, 0, 100)")
printArrayInLine(slice(a, 0, 100))
 
printSeparator()
print("slice(a, 100, 0)")
printArrayInLine(slice(a, 100, 0))

Z výsledků zobrazených pod tímto odstavcem je patrné, že pokud horní či dolní index přesahuje meze pole, použije se index 1 popř. index odpovídající délce pole (#pole):

-------------------------------
Original array
1, 2, 3, 4, 5, 6, 7, 8, 9, 10
-------------------------------
slice(a)
1, 2, 3, 4, 5, 6, 7, 8, 9, 10
-------------------------------
slice(a, 5)
5, 6, 7, 8, 9, 10
-------------------------------
slice(a, 5, 5)
5
-------------------------------
slice(a, 4, 8)
4, 5, 6, 7, 8
-------------------------------
slice(a, 0, 100)
1, 2, 3, 4, 5, 6, 7, 8, 9, 10
-------------------------------
slice(a, 100, 0)
 

14. Získání unikátních prvků v poli

Další potenciálně velmi užitečnou operací je získání unikátních prvků v poli, tj. přečtení nového pole, v němž se každá hodnota vyskytuje maximálně jedenkrát. Právě díky této operaci lze s poli pracovat stejně jako s množinami. Prvky v poli přitom nemusí být nijak uspořádány, což je ostatně patrné i z následujícího demonstračního příkladu:

-- načtení knihovny Moses a současně import symbolů do globálního jmenného prostoru
M = require "moses"
M.import()
 
 
-- tisk obsahu pole
function printArrayInLine(array)
    for i, value in ipairs(array) do
        io.write(value)
        if i ~= #array then
            io.write(", ")
        end
    end
    print()
end
 
 
-- oddělení obsahu
function printSeparator()
    print("-------------------------------")
end
 
 
a = range(10)
printSeparator()
print("Original array")
printArrayInLine(a)
print("unique(a)")
printArrayInLine(unique(a))
 
a = {1, 2, 3, 1, 2, 3}
printSeparator()
print("Original array")
printArrayInLine(a)
print("unique(a)")
printArrayInLine(unique(a))
 
a = {3, 2, 1, 1, 2, 3}
printSeparator()
print("Original array")
printArrayInLine(a)
print("unique(a)")
printArrayInLine(unique(a))

Povšimněte si, jakým způsobem jsou uspořádány prvky ve výsledném poli. Z tohoto uspořádání lze vyčíst, jak vlastně pracuje algoritmus, nad nímž je funkce unique postavena:

-------------------------------
Original array
1, 2, 3, 4, 5, 6, 7, 8, 9, 10
unique(a)
1, 2, 3, 4, 5, 6, 7, 8, 9, 10
-------------------------------
Original array
1, 2, 3, 1, 2, 3
unique(a)
1, 2, 3
-------------------------------
Original array
3, 2, 1, 1, 2, 3
unique(a)
3, 2, 1

15. Výběr prvků ze začátku pole funkcí vyššího řádu selectWhile

Z polí je možné získat začátek či naopak zbytek pole s využitím funkcí nazvaných selectWhile a dropWhile. Těmto funkcím je zapotřebí ve druhé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é v sedmnácté kapitole). V případě druhého parametru předávaného do selectWhile a dropWhile 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.

Rozdíly oproti funkcím z knihovny Lua Fun jsou shrnuty v tabulce:

# Funkce z Lua Fun Funkce z Moses Stručný popis
1 take_while(predikát, sekvence) selectWhile(pole, predikát) získání prvků ze začátku sekvence na základě zadané podmínky
2 drop_while(predikát, sekvence) dropWhile(pole, predikát) získání nové sekvence získané přeskočením prvků na začátku základě podmínky
Poznámka: povšimněte si, že je odlišné i pořadí parametrů předávaných do těchto funkcí. To je poměrně nešťastná situace.

V dalším demonstračním příkladu si ukážeme základní chování funkce vyššího řádu selectWhile:

-- načtení knihovny Moses a současně import symbolů do globálního jmenného prostoru
M = require "moses"
M.import()
 
 
-- tisk obsahu pole
function printArrayInLine(array)
    for i, value in ipairs(array) do
        io.write(value)
        if i ~= #array then
            io.write(", ")
        end
    end
    print()
end
 
 
-- oddělení obsahu
function printSeparator()
    print("--------------------------------------------")
end
 
 
a = range(10)
printSeparator()
print("Original array")
printArrayInLine(a)
 
printSeparator()
print("selectWhile(a, function (x) return x<=5 end)")
printArrayInLine(selectWhile(a, function (x) return x<=5 end))
 
printSeparator()
print("selectWhile(a, function (x) return x%2 == 1 end)")
printArrayInLine(selectWhile(a, function (x) return x%2 == 1 end))
 
printSeparator()
print("selectWhile(a, function (x) return true end)")
printArrayInLine(selectWhile(a, function (x) return true end))
 
printSeparator()
print("selectWhile(a, function (x) return false end)")
printArrayInLine(selectWhile(a, function (x) return false end))

Výsledky získané po spuštění ukazují, jak se tato funkce chová:

--------------------------------------------
Original array
1, 2, 3, 4, 5, 6, 7, 8, 9, 10
--------------------------------------------
selectWhile(a, function (x) return x<=5 end)
1, 2, 3, 4, 5
--------------------------------------------
selectWhile(a, function (x) return x%2 == 1 end)
1
--------------------------------------------
selectWhile(a, function (x) return true end)
1, 2, 3, 4, 5, 6, 7, 8, 9, 10
--------------------------------------------
selectWhile(a, function (x) return false end)
Poznámka: v posledním příkladu je vráceno prázdné pole, protože predikát již od začátku vrátil hodnotu false, tudíž je další zpracování prvků pole ukončeno.

16. Přeskočení prvků ze začátku pole funkcí vyššího řádu dropWhile

Logickým opakem funkce selectWhile z předchozí kapitoly je funkce nazvaná dropWhile, která dokáže na základě hodnoty vracené z predikátu (typicky z anonymní funkce) přeskočit prvky na začátku sekvence. Až na opačné pořadí parametrů se tato funkce chová podobně jako prakticky stejně pojmenovaná funkce drop_while z knihovny Lua Fun, takže se ihned podívejme na demonstrační příklad:

-- načtení knihovny Moses a současně import symbolů do globálního jmenného prostoru
M = require "moses"
M.import()
 
 
-- tisk obsahu pole
function printArrayInLine(array)
    for i, value in ipairs(array) do
        io.write(value)
        if i ~= #array then
            io.write(", ")
        end
    end
    print()
end
 
 
-- oddělení obsahu
function printSeparator()
    print("--------------------------------------------")
end
 
 
a = range(10)
printSeparator()
print("Original array")
printArrayInLine(a)
 
printSeparator()
print("dropWhile(a, function (x) return x<=5 end)")
printArrayInLine(dropWhile(a, function (x) return x<=5 end))
 
printSeparator()
print("dropWhile(a, function (x) return x%2 == 1 end)")
printArrayInLine(dropWhile(a, function (x) return x%2 == 1 end))
 
printSeparator()
print("dropWhile(a, function (x) return true end)")
printArrayInLine(dropWhile(a, function (x) return true end))
 
printSeparator()
print("dropWhile(a, function (x) return false end)")
printArrayInLine(dropWhile(a, function (x) return false end))

Z vytištěných zpráv je patrné, že je skutečně možné přeskočit prvky na začátku pole (první výsledek), popř. dokonce získat prázdné pole (předposlední výsledek) či pole odpovídající poli původnímu (výsledek poslední):

--------------------------------------------
Original array
1, 2, 3, 4, 5, 6, 7, 8, 9, 10
--------------------------------------------
dropWhile(a, function (x) return x<=5 end)
6, 7, 8, 9, 10
--------------------------------------------
dropWhile(a, function (x) return x%2 == 1 end)
2, 3, 4, 5, 6, 7, 8, 9, 10
--------------------------------------------
dropWhile(a, function (x) return true end)
 
--------------------------------------------
dropWhile(a, function (x) return false end)
1, 2, 3, 4, 5, 6, 7, 8, 9, 10

17. Výběr prvků z pole s využitím funkce vyššího řádu filter

Poslední funkcí, kterou si v dnešním článku popíšeme, je funkce nazvaná filter. I s touto funkcí jsme se seznámili minule při popisu knihovny Lua Fun, takže si jen v krátkosti shrňme její základní vlastnosti. Tato funkce postupně prochází všemi prvky pole a pro každý takto získaný prvek (resp. přesněji řečeno pro jeho hodnotu) volá nějakou programátorem zvolenou funkci – takzvaný predikát. V případě, že tato funkce vrátí pravdivostní hodnotu true je prvek vložen do nového pole, které je výsledkem funkce filter. Pokud naopak funkce/predikát vrátí hodnotu false nebo nil, je takový prvek ignorován. Kvůli tomu, že do funkce filter předáváme jinou funkci, je filter funkcí vyššího řádu. Ukažme si příklad použití:

-- načtení knihovny Moses a současně import symbolů do globálního jmenného prostoru
M = require "moses"
M.import()
 
 
-- tisk obsahu pole
function printArrayInLine(array)
    for i, value in ipairs(array) do
        io.write(value)
        if i ~= #array then
            io.write(", ")
        end
    end
    print()
end
 
 
-- oddělení obsahu
function printSeparator()
    print("--------------------------------------------")
end
 
 
a = range(10)
printSeparator()
print("Original array")
printArrayInLine(a)
 
printSeparator()
print("filter(a, function (x) return x<=5 end)")
printArrayInLine(filter(a, function (x) return x<=5 end))
 
printSeparator()
print("filter(a, function (x) return x%2 == 1 end)")
printArrayInLine(filter(a, function (x) return x%2 == 1 end))
 
printSeparator()
print("filter(a, function (x) return true end)")
printArrayInLine(filter(a, function (x) return true end))
 
printSeparator()
print("filter(a, function (x) return false end)")
printArrayInLine(filter(a, function (x) return false end))

Výsledky získané po spuštění tohoto demonstračního příkladu:

--------------------------------------------
Original array
1, 2, 3, 4, 5, 6, 7, 8, 9, 10
--------------------------------------------
filter(a, function (x) return x<=5 end)
1, 2, 3, 4, 5
--------------------------------------------
filter(a, function (x) return x%2 == 1 end)
1, 3, 5, 7, 9
--------------------------------------------
filter(a, function (x) return true end)
1, 2, 3, 4, 5, 6, 7, 8, 9, 10
--------------------------------------------
filter(a, function (x) return false end)
Poznámka: v posledním příkladu bylo získáno prázdné pole, což je očekávané chování, neboť predikát pro všechny prvky (jejich hodnoty) vracel pravdivostní hodnotu false.

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 test korektní instalace knihovny Moses https://github.com/tisnik/functional-lua/tree/master/moses/01_in­stall_test.lua
2 02_import_everything.lua import funkcí do globálního jmenného prostoru https://github.com/tisnik/functional-lua/tree/master/moses/02_im­port_everything.lua
3 03_import_into_context.lua import funkcí do zvoleného kontextu https://github.com/tisnik/functional-lua/tree/master/moses/03_im­port_into_context.lua
4 04_zeros.lua vytvoření pole s nulovými prvky https://github.com/tisnik/functional-lua/tree/master/moses/04_zeros.lua
5 05_ones.lua vytvoření pole se všemi prvky nastavenými na jedničku https://github.com/tisnik/functional-lua/tree/master/moses/05_ones.lua
6 06_rep.lua vytvoření pole s opakujícími se prvky stejné hodnoty https://github.com/tisnik/functional-lua/tree/master/moses/06_rep.lua
7 07_vector.lua vytvoření pole s opakujícími se prvky stejné hodnoty https://github.com/tisnik/functional-lua/tree/master/moses/07_vector.lua
8 08_range.lua funkce pro vytvoření sekvence číselných hodnot v poli https://github.com/tisnik/functional-lua/tree/master/moses/08_range.lua
9 09_reverse_range.lua využití funkce reverse pro otočení obsahu pole https://github.com/tisnik/functional-lua/tree/master/moses/09_re­verse_range.lua
10 10_first_last.lua získání prvních či posledních n prvků pole https://github.com/tisnik/functional-lua/tree/master/moses/10_fir­st_last.lua
11 11_initial_rest.lua získání prvků ze začátku či z konce pole https://github.com/tisnik/functional-lua/tree/master/moses/11_i­nitial_rest.lua
12 12_set_operations.lua pole použité ve funkci množiny (set) https://github.com/tisnik/functional-lua/tree/master/moses/12_set_o­perations.lua
13 13_stack.lua pole použité ve funkci zásobníku (stack) https://github.com/tisnik/functional-lua/tree/master/moses/13_stack.lua
14 14_queue.lua pole použité ve funkci fronty (queue) https://github.com/tisnik/functional-lua/tree/master/moses/14_queue.lua
15 15_queue_push_pop.lua alternativní pojmenování některých operací https://github.com/tisnik/functional-lua/tree/master/moses/15_qu­eue_push_pop.lua
16 16_slice.lua provedení řezu pole https://github.com/tisnik/functional-lua/tree/master/moses/16_slice.lua
17 17_unique.lua získání nového pole s unikátními prvky https://github.com/tisnik/functional-lua/tree/master/moses/17_unique.lua
18 18_select_while.lua výběr prvků z pole na základě funkce vyššího řádu select_while https://github.com/tisnik/functional-lua/tree/master/moses/18_se­lect_while.lua
19 19_drop_while.lua výběr prvků z pole na základě funkce vyššího řádu drop_while https://github.com/tisnik/functional-lua/tree/master/moses/19_drop_whi­le.lua
20 20_filter.lua funkce vyššího řádu filter pro výběr prvků z pole https://github.com/tisnik/functional-lua/tree/master/moses/20_filter.lua

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

Předchozí dva články o funkcionálním stylu programování podporovaného knihovnou Lua Fun:

  1. Lua Fun: knihovna pro zpracování konečných i nekonečných sekvencí v jazyce Lua
    https://www.root.cz/clanky/lua-fun-knihovna-pro-zpracovani-konecnych-i-nekonecnych-sekvenci-v-jazyce-lua/
  2. Lua Fun: knihovna pro zpracování konečných i nekonečných sekvencí v jazyce Lua (dokončení)
    https://www.root.cz/clanky/lua-fun-knihovna-pro-zpracovani-konecnych-i-nekonecnych-sekvenci-v-jazyce-lua-dokonceni/

Programovacím jazykem Lua jsme se již na stránkách Rootu poměrně podrobně zabývali v samostatném seriálu. Jedná se o snadno naučitelný jazyk, který je ovšem (mj. i díky konceptu metatabulek) rozšiřitelný a poměrně tvárný. Viz též následující odkazy na již vydané články (včetně odkazu na e-book, jenž na základě těchto článků později vznikl):

MIF obecny

  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ů (které jsou poněkud paradoxně součástí seriálu o programovacím jazyku Java a JVM):

  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. To je v případě dalších ekosystémů (Java, Python, Ruby, …) prakticky nemožné, resp. to není v silách jediného autora :-)

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