Underscore aneb další knihovna pro funkcionální programování v JavaScriptu

Pavel Tišnovský 8. 3. 2016

Na články o knihovně Mori navážeme popisem další zajímavé knihovny určené pro podporu funkcionálního programování v JavaScriptu. Jmenuje se Underscore a přímo pracuje s datovými strukturami JavaScriptu.

Obsah

1. Underscore aneb další knihovna pro funkcionální programování v JavaScriptu

2. Mori versus Underscore: dva různé přístupy k podobné problematice

3. Základní funkce pro „funkcionální“ práci s poli

4. Funkce uniq

5. První demonstrační příklad

6. Různé varianty funkce range

7. Druhý demonstrační příklad

8. Vyhledávání prvků v polích

9. Třetí demonstrační příklad

10. Množinové operace s poli

11. Čtvrtý demonstrační příklad

12. Zploštění vnořených polí, kombinace prvků z různých polí atd.

13. Pátý demonstrační příklad

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

15. Odkazy na Internetu

1. Underscore aneb další knihovna pro funkcionální programování v JavaScriptu

V postupně vznikajícím miniseriálu o různých knihovnách, frameworcích a transpřekladačích určených pro stále populárnější programovací jazyk JavaScript jsme se prozatím zabývali čtyřmi zajímavými projekty. Nejprve jsme si popsali takzvaný transpřekladač (transcompiler) pojmenovaný lua2js, který transformuje kód z programovacího jazyka Lua do JavaScriptu. Dále jsme se zmínili o virtuálním stroji jazyka Lua naprogramovaném v JavaScriptu, který se jmenuje lua.vm.js.

Dalším projektem byl opět transpřekladač nazvaný Wisp, který slouží pro transformaci zdrojového kódu z jazyka syntaxí podobného LISPu, Scheme či Clojure do JavaScriptu, přičemž transformovaný kód je stále velmi dobře čitelný (u některých dalších transpřekladačů toto pravidlo neplatí). Posledním prozatím popsaným projektem je knihovna pojmenovaná Mori, která do JavaScriptu přináší perzistentní a neměnitelné datové struktury používané v jazyku Clojure, resp. přesněji řečeno ClojureScriptu. Ve skutečnosti Mori implementaci perzistentních datových struktur přímo z ClojureScriptu přebírá a jen nepatrně je upravuje a přidává snadno použitelné JavaScriptové API.

Pro podrobnější informace o výše zmíněných projektech lua2js, lua.vm.js, Wisp a Mori se stačí podívat na následující pětici článků, které již zde na Rootu vyšly:

  1. Programovací jazyk Lua v roli skriptovacího jazyka pro WWW stránky (popis projektů lua2js a lua.vm.js)
  2. Propojení světa LISPu se světem JavaScriptu s využitím transpřekladače Wisp (1.část)
  3. Propojení světa LISPu se světem JavaScriptu s využitím transpřekladače Wisp (2.část)
  4. Projekt Mori aneb perzistentní datové struktury pro JavaScript
  5. Projekt Mori aneb perzistentní datové struktury pro JavaScript (dokončení)

2. Mori versus Underscore: dva různé přístupy k podobné problematice

Již zmíněný projekt Mori je sice postaven na robustních knihovnách ClojureScriptu, v nichž jsou implementovány perzistentní seznamy, vektory, fronty, množiny a mapy, to však může být v některých případech i jeho nevýhoda, protože i „minifikovaná“ verze této knihovny je relativně velká (konkrétně 185 kB). Alternativu, která je dnes s velkou pravděpodobností mezi javascriptisty více populární, představuje například knihovna nazvaná Underscore či též Underscore.js. Mimochodem, jméno této knihovny je odvozeno od toho, že se ke všem funkcím přistupuje přes objekt pojmenovaný jednoduše „_“ (kombinace s JQuery apod. je tedy možná, zdrojový kód pak začne být pěkně přeplněn různými divnými symboly :-).

Knihovna Underscore je navržena poměrně minimalistickým způsobem, což konkrétně znamená, že současná „minifikovaná“ verze této knihovny má velikost necelých šestnácti kilobajtů (první verze měla dokonce jen čtyři kilobajty). Tyto rozdíly se sice mohou zdát malé, ovšem u komplikovanější aplikace (navíc přenášené někdy přes pomalé a drahé připojení) se počítá každý ušetřený kilobajt (ale možná jen jsem ze staré školy). V knihovně Underscore najdeme mnoho funkcí pro práci s klasickými JavaScriptovými poli, což sice jsou měnitelné struktury, ovšem samotná knihovna Underscore se k polím chová „funkcionálně“, takže jejich obsah nikdy nemodifikuje (navíc je práce s poli velmi efektivní). Pole se zde používají i ve funkci množin apod. Další části knihovny Underscore si popíšeme až v navazujících kapitolách a taktéž v navazujícím článku, takže se nyní věnujme především polím.

Společné vlastnosti a rozdíly mezi knihovnami Mori a Underscore lze shrnout (s určitým zjednodušením) takto:

Vlastnost Mori Underscore
Velikost knihovny 185 kB 16 kB
Rychlost (1 thread) menší vyšší
Perzistentní struktury ano ne
Lazy sekvence ano ne
Nekonečné (lazy) sekvence ano ne
Založeno na struktury ClojureScriptu Array v JavaScriptu
Rozšíření std. objektů JS ne ne
Funkce typu range ano ano
Funkce typu map, reduce ano ano
Funkce typu filter ano ano
Vyhledávání v sekvencích ano ano
Predikáty some a every ano ano
Množinové operace ano ano
Memoizace (cache výsledků) nyní ne explicitní
Další vlastnosti mapy, množiny funkce pro lepší práci s JS objekty

3. Základní funkce pro „funkcionální“ práci s poli

V mnoha algoritmech, zejména pak v algoritmech založených na zpracování seznamů (list), zásobníků (stack) a front (queue), se velmi často přistupuje k prvnímu popř. poslednímu prvku nějaké datové sekvence a taktéž se získává původní sekvence bez prvního (popř. posledního) prvku. Pro tyto účely je v knihovně Underscore.js implementována následující čtveřice funkcí (funkce s různým počtem argumentů rozepisuji na více řádků):

# Funkce Význam
1 _.first(pole) vrací první prvek pole
2 _.first(pole, n) vrací prvních n prvků pole
3 _.last(pole) vrací poslední prvek pole
4 _.last(pole, n) vrací posledních n prvků pole
5 _.initial(pole) vrací kopii pole bez posledního prvku
6 _.initial(pole, n) vrací kopii pole bez posledních n prvků
7 _.rest(pole) vrací kopii pole bez prvního prvku
8 _.rest(pole) vrací kopii pole bez prvních n prvků

Pro některé funkce existují i jmenné aliasy:

# Funkce Alias(y)
1 first head, take
2 rest tail, drop

4. Funkce uniq

Při tvorbě mnoha aplikací se setkáme s nutností odstranit z pole duplicitní prvky. Pro tyto účely lze použít funkci nazvanou jednoduše uniq. Této funkci se předává pole, výsledkem je nové pole bez duplicitních prvků, přičemž za duplicitní prvky jsou považovány všechny dvojice, kde platí X===Y (=== je JavaScriptový operátor). V případě, že je pole nesetříděno, je použit algoritmus s větší složitostí (ostatně sami se podívejte na jeho implementaci). Pokud je však zapotřebí odstranit duplicitní prvky z již setříděného pole, může knihovna Underscore použít rychlejší algoritmus, což se samozřejmě pozitivně projeví především u delších polí (příkladem může být například klientská strana GIS s mnoha grafickými objekty). V tomto případě je nutné funkci uniq předat ještě druhý argument s pravdivostní hodnotou true, aby byl rychlejší algoritmus vybrán:

# Funkce Význam
1 _.uniq(pole) vrací nové pole s unikátními prvky
2 _.uniq(pole, true) dtto, ovšem rychlejší u setříděného pole

5. První demonstrační příklad

V dnešním prvním demonstračním příkladu je ukázána práce s výše popsanými funkcemi first, last, initial, rest a taktéž uniq. Příklad je napsán takovým způsobem, aby ho bylo možné spustit přímo ve webovém prohlížeči, například přes tuto jednoduchou webovou stránku:

<!doctype html>
<html>
    <head>
        <title>Underscore tests #01</title>
        <meta charset="utf-8">
        <script src="underscore-min.js"></script>
        <script src="underscore_01.js"></script>
    </head>
    <body>
        <h1>Underscore tests #01</h1>
    </body>
</html>

Následuje výpis zdrojového kódu tohoto demonstračního příkladu. Povšimněte si použití eval ve funkci nazvané printArrayInfo(); podobný kód jsme již viděli u popisu knihovny Mori:

// ------------------------------------------------------------
// Knihovna Underscore.js: demonstracni priklad cislo 1
//                         Zakladni funkce pri praci s poli.
// ------------------------------------------------------------
 
 
 
// funkce pro vypis informaci o vybranem poli (ci jinem objektu)
function printArrayInfo(expression) {
    var anArray = eval(expression);
    console.log("---------------------------------");
    console.log(expression);
 
    // zjisteni typu objektu (a pripadne delky pole)
    if (anArray instanceof Array) {
        console.log("type:    array");
        console.log("length:  " + anArray.length);
    }
    // jiny typ objektu, nemame zde jistotu, ze existuje atribut length
    else {
        console.log("type:    " + typeof anArray);
    }
    console.log("content: " + anArray);
}
 
 
 
// vytvoreni bezneho pole, nad kterym se budou provadet nektere operace
var array1 = [1,2,3,4,5];
 
// informace o samotnem vstupnim poli
printArrayInfo(array1);
 
// zakladni operace nad polem
 
// funkce _.first()
printArrayInfo("_.first(array1)");
printArrayInfo("_.first(array1, 2)");
printArrayInfo("_.first(array1, 100)"); // n je vetsi nez delka pole
 
// funkce _.last()
printArrayInfo("_.last(array1)");
printArrayInfo("_.last(array1, 2)");
printArrayInfo("_.last(array1, 100)"); // n je vetsi nez delka pole
 
// funkce _.initial()
printArrayInfo("_.initial(array1)");
printArrayInfo("_.initial(array1, 2)");
printArrayInfo("_.initial(array1, 100)"); // n je vetsi nez delka pole
 
// funkce _.rest()
printArrayInfo("_.rest(array1)");
printArrayInfo("_.rest(array1, 2)");
printArrayInfo("_.rest(array1, 100)"); // n je vetsi nez delka pole
 
 
// otestovani funkce uniq
var array2 = [1,2,3,3,2,1];
var array3 = [3,2,1,1,2,3];
var array4 = [1,2,2,3,3,3,4,4,4,4];
 
// informace o samotnem vstupnim poli
printArrayInfo(array2);
printArrayInfo(array3);
printArrayInfo(array4);
 
// vytvoreni pole s unikatnimi hodnotami
printArrayInfo("_.uniq(array2)");
printArrayInfo("_.uniq(array3)");
 
// toto pole je setrideno, muzeme pouzit rychlejsi algoritmus
printArrayInfo("_.uniq(array4, true)");
 
// plati o pro pole retezcu atd.
var names = ["Perl", "Python", "Java", "JavaScript", "C", "Lua", "Clojure", "Lua", "C"];
printArrayInfo("names");
printArrayInfo("_.uniq(names)");
 
 
 
// ------------------------------------------------------------
// Finito
// ------------------------------------------------------------

Výsledek běhu prvního demonstračního příkladu může vypadat následovně (konzoli je možné zobrazit například přes Firebug apod.):

---------------------------------
[1, 2, 3, 4, 5]
type:    array
length:  5
content: 1,2,3,4,5
---------------------------------
_.first(array1)
type:    number
content: 1
---------------------------------
_.first(array1, 2)
type:    array
length:  2
content: 1,2
---------------------------------
_.first(array1, 100)
type:    array
length:  5
content: 1,2,3,4,5
---------------------------------
_.last(array1)
type:    number
content: 5
---------------------------------
_.last(array1, 2)
type:    array
length:  2
content: 4,5
---------------------------------
_.last(array1, 100)
type:    array
length:  5
content: 1,2,3,4,5
---------------------------------
_.initial(array1)
type:    array
length:  4
content: 1,2,3,4
---------------------------------
_.initial(array1, 2)
type:    array
length:  3
content: 1,2,3
---------------------------------
_.initial(array1, 100)
type:    array
length:  0
content:
---------------------------------
_.rest(array1)
type:    array
length:  4
content: 2,3,4,5
---------------------------------
_.rest(array1, 2)
type:    array
length:  3
content: 3,4,5
---------------------------------
_.rest(array1, 100)
type:    array
length:  0
content:
---------------------------------
[1, 2, 3, 3, 2, 1]
type:    array
length:  6
content: 1,2,3,3,2,1
---------------------------------
[3, 2, 1, 1, 2, 3]
type:    array
length:  6
content: 3,2,1,1,2,3
---------------------------------
[1, 2, 2, 3, 3, 3, 4, 4, 4, 4]
type:    array
length:  10
content: 1,2,2,3,3,3,4,4,4,4
---------------------------------
_.uniq(array2)
type:    array
length:  3
content: 1,2,3
---------------------------------
_.uniq(array3)
type:    array
length:  3
content: 3,2,1
---------------------------------
_.uniq(array4, true)
type:    array
length:  4
content: 1,2,3,4
---------------------------------
names
type:    array
length:  9
content: Perl,Python,Java,JavaScript,C,Lua,Clojure,Lua,C
---------------------------------
_.uniq(names)
type:    array
length:  7
content: Perl,Python,Java,JavaScript,C,Lua,Clojure

6. Různé varianty funkce range

V knihovně Underscore nalezneme i velmi užitečnou funkci nazvanou range, která zde slouží pro vytvoření pole obsahujícího sekvenci číselných hodnot (výsledkem je tedy skutečné pole, nikoli iterátor). Užitečnost této funkce se ve skutečnosti ještě řádově zvýší ve chvíli, kdy se (už příště) seznámíme s funkcemi map, reduce, filter, each a zip, které knihovna Underscore samozřejmě taktéž nabízí. Funkci range je možné předat počáteční hodnotu, mezní hodnotu a taktéž krok (rozdíl hodnoty mezi dvěma prvky pole), ovšem dva argumenty, konkrétně počáteční hodnota a krok jsou ve skutečnosti nepovinné.

Zajímavé je, že počáteční hodnota, mezní hodnota i krok mohou být reálná čísla, takže se tato funkce svým chováním odlišuje například od stejně pojmenované funkce range známé z Pythonu 2.x (a samozřejmě ještě mnohem více od stejnojmenné funkce v Pythonu 3.x, která má specifické chování). V následující tabulce jsou ukázány některé možnosti volání funkce range z knihovny Underscore:

# Funkce Význam
1 _.range(stop) vrací nové pole s prvky 0, 1, … stop-1
2 _.range(start, stop) vrací nové pole s prvky start, start+1, … stop-1
3 _.range(start, stop, step) vrací nové pole s prvky start, start+step, start+2*step, až po mez (ta není překročena)

Poznámky:

  • Všechny tři argumenty jsou typu Number (základní datový typ JavaScriptu), tj. jedná se o reálná čísla typicky typu double. Neexistuje zde omezení na použití celých čísel (na rozdíl například od Pythonu).
  • Po zavolání range(0) se podle očekávání vytvoří prázdné pole.
  • Pokud platí podmínka start>stop a krok je kladný, opět se vytvoří prázdné pole.
  • Pokud je start>stop a krok je záporný, vytvoří se běžná sekvence.
  • Vzhledem k tomu, že se hlídá překročení zadané meze a nikoli její přesné dosažení, lze pro zadání kroku použít i v jiných případech problematické hodnoty typu 0,1 (hodnoty vytvořených prvků sice logicky nebudou přesné, ovšem alespoň nedojde k nekonečné smyčce).

7. Druhý demonstrační příklad

Většina možných a podporovaných variant výše popsané funkce range je vysvětlena ve druhém demonstračním příkladu, jehož úplný zdrojový kód je vypsán pod tímto odstavcem:

// ------------------------------------------------------------
// Knihovna Underscore.js: demonstracni priklad cislo 2
//                         Pouziti funkce range().
// ------------------------------------------------------------
 
 
 
// funkce pro vypis informaci o vybranem poli (ci jinem objektu)
function printArrayInfo(expression) {
    var anArray = eval(expression);
    console.log("---------------------------------");
    console.log(expression);
 
    // zjisteni typu objektu (a pripadne delky pole)
    if (anArray instanceof Array) {
        console.log("type:    array");
        console.log("length:  " + anArray.length);
    }
    // jiny typ objektu, nemame zde jistotu, ze existuje atribut length
    else {
        console.log("type:    " + typeof anArray);
    }
    console.log("content: " + anArray);
}
 
 
 
// informace o polich vytvorenych funkci range
printArrayInfo("_.range(10)");
printArrayInfo("_.range(-10)");
printArrayInfo("_.range(0, 10)");
printArrayInfo("_.range(10, 0)");
printArrayInfo("_.range(10, 0, -1)");
printArrayInfo("_.range(0, 10, 2)");
printArrayInfo("_.range(0, 10, -2)");
printArrayInfo("_.range(10, 0, 2)");
printArrayInfo("_.range(10, 0, -2)");
printArrayInfo("_.range(0, 10, 100)");
 
// lze pouzit i desetinna cisla
printArrayInfo("_.range(10, 15, 0.5)");
printArrayInfo("_.range(10,  5,-0.5)");
 
// desetinna cisla pro urceni zacatku i konce
printArrayInfo("_.range(1.5, 3.5, 1/2)");
printArrayInfo("_.range(3.2, 1.8, -1/4)");
printArrayInfo("_.range(1/2, 3/4, 1/10)");
 
// stara znama chyba se zde projevuje jen castecne
printArrayInfo("_.range(0, 10, 0.1)");
 
 
// ------------------------------------------------------------
// Finito
// ------------------------------------------------------------

Poznámka: 1/2, –1/4 atd. jsou v JavaScriptu výrazy, které je zde možné bez problémů použít.

Výsledek běhu druhého demonstračního příkladu může vypadat následovně (konzoli je opět možné zobrazit například přes Firebug apod.):

---------------------------------
_.range(10)
type:    array
length:  10
content: 0,1,2,3,4,5,6,7,8,9
---------------------------------
_.range(-10)
type:    array
length:  0
content:
---------------------------------
_.range(0, 10)
type:    array
length:  10
content: 0,1,2,3,4,5,6,7,8,9
---------------------------------
_.range(10, 0)
type:    array
length:  0
content:
---------------------------------
_.range(10, 0, -1)
type:    array
length:  10
content: 10,9,8,7,6,5,4,3,2,1
---------------------------------
_.range(0, 10, 2)
type:    array
length:  5
content: 0,2,4,6,8
---------------------------------
_.range(0, 10, -2)
type:    array
length:  0
content:
---------------------------------
_.range(10, 0, 2)
type:    array
length:  0
content:
---------------------------------
_.range(10, 0, -2)
type:    array
length:  5
content: 10,8,6,4,2
---------------------------------
_.range(0, 10, 100)
type:    array
length:  1
content: 0
---------------------------------
_.range(10, 15, 0.5)
type:    array
length:  10
content: 10,10.5,11,11.5,12,12.5,13,13.5,14,14.5
---------------------------------
_.range(10,  5,-0.5)
type:    array
length:  10
content: 10,9.5,9,8.5,8,7.5,7,6.5,6,5.5
---------------------------------
_.range(1.5, 3.5, 1/2)
type:    array
length:  4
content: 1.5,2,2.5,3
---------------------------------
_.range(3.2, 1.8, -1/4)
type:    array
length:  6
content: 3.2,2.95,2.7,2.45,2.2,1.9500000000000002
---------------------------------
_.range(1/2, 3/4, 1/10)
type:    array
length:  3
content: 0.5,0.6,0.7
---------------------------------
_.range(0, 10, 0.1)
type:    array
length:  100
content: 0,0.1,0.2,0.30000000000000004,0.4,0.5,0.6,0.7,0.7999999999999999,0.8999999999999999,
0.9999999999999999,1.0999999999999999,1.2,1.3,1.4000000000000001,1.5000000000000002,
1.6000000000000003,1.7000000000000004,1.8000000000000005,1.9000000000000006,2.0000000000000004,
2.1000000000000005,2.2000000000000006,2.3000000000000007,2.400000000000001,2.500000000000001,
2.600000000000001,2.700000000000001,2.800000000000001,2.9000000000000012,3.0000000000000013,
3.1000000000000014,3.2000000000000015,3.3000000000000016,3.4000000000000017,3.5000000000000018,
3.600000000000002,3.700000000000002,3.800000000000002,3.900000000000002,4.000000000000002,
4.100000000000001,4.200000000000001,4.300000000000001,4.4,4.5,4.6,4.699999999999999,
4.799999999999999,4.899999999999999,4.999999999999998,5.099999999999998,5.1999999999999975,
5.299999999999997,5.399999999999997,5.4999999999999964,5.599999999999996,5.699999999999996,
5.799999999999995,5.899999999999995,5.999999999999995,6.099999999999994,6.199999999999994,
6.299999999999994,6.399999999999993,6.499999999999993,6.5999999999999925,6.699999999999992,
6.799999999999992,6.8999999999999915,6.999999999999991,7.099999999999991,7.19999999999999,
7.29999999999999,7.39999999999999,7.499999999999989,7.599999999999989,7.699999999999989,
7.799999999999988,7.899999999999988,7.999999999999988,8.099999999999987,8.199999999999987,
8.299999999999986,8.399999999999986,8.499999999999986,8.599999999999985,8.699999999999985,
8.799999999999985,8.899999999999984,8.999999999999984,9.099999999999984,9.199999999999983,
9.299999999999983,9.399999999999983,9.499999999999982,9.599999999999982,9.699999999999982,
9.799999999999981,9.89999999999998

8. Vyhledávání prvků v polích

Na první pohled jednoduché, ale o to užitečnější mohou být v praxi funkce určené pro vyhledávání prvků v polích. Knihovna Underscore programátorům nabízí celkem čtyři tyto funkce, které se od sebe odlišují tím, zda vyhledávají prvky od začátku pole, od konce pole a taktéž tím, zda se prvek vyhledává přímo podle své hodnoty či na základě nějakého predikátu, tj. funkce postupně volané pro jednotlivé prvky. V každém případě platí, že po nalezení prvního vhodného prvku se vyhledání zastaví a daná funkce vrátí index tohoto prvku. Pokud prvek není nalezen, vrátí se hodnota –1, tj. neexistující index (prvky v běžných polích jsou v JavaScriptu indexovány od nuly). Podívejme se nyní na všechny čtyři vyhledávací funkce:

# Funkce Význam
1 _.indexOf(pole, hodnota) vyhledávání prvního prvku s danou hodnotou od začátku pole
2 _.indexOf(pole, hodnota, index) vyhledávání prvního prvku s danou hodnotou od zadaného indexu směrem ke konci pole
3 _.indexOf(pole, hodnota, true) binární vyhledávání v setříděném poli
4 _.lastIndexOf(pole, hodnota) vyhledávání posledního prvku s danou hodnotou
5 _.lastIndexOf(pole, hodnota, index) vyhledávání posledního prvku od zadaného indexu
6 _.findIndex(pole, predikát) nalezení prvního prvku, pro nějž predikát vrátil hodnotu true
7 _.findLastIndex(pole, predikát) nalezení posledního prvku, pro nějž predikát vrátil hodnotu true

Poznámka: použití funkce findIndex s vhodným predikátem dokáže z vytvářených programů odstranit poměrně velké množství programových smyček.

9. Třetí demonstrační příklad

Všechny čtyři funkce popsané v předchozí kapitole jsou použity v dnešním třetím demonstračním příkladu, jehož zdrojový kód je vypsán pod tímto odstavcem. Povšimněte si především použití vlastních predikátů (funkcí vracejících pro každý prvek true či false) u funkcí findIndex a findLastIndex. Dva z těchto predikátů vrací konstantu bez ohledu na to, jaký prvek je jim předán:

// ------------------------------------------------------------
// Knihovna Underscore.js: demonstracni priklad cislo 3
//                         Pouziti funkci pro vyhledani prvku v polich.
// ------------------------------------------------------------
 
 
 
var names = ["C", "C++", "C#", "Java",
             "Perl", "Python", "Ruby",
             "JavaScript", "CoffeeScript", "Dart", "Wisp",
             // prvky se opakuji
             "C", "C++", "C#", "Java",
             "Lua", "Clojure", "Lisp", "Forth"];
 
 
 
function printArrayItem(anArray, index)
{
    console.log("#" + i + " = " + anArray[i]);
}
 
var i;
 
 
 
console.log("_.indexOf():");
 
// bezne vyhledavani prvniho prvku
i = _.indexOf(names, "Java");
printArrayItem(names, i);
 
// vyhledavani od desateho prvku
i = _.indexOf(names, "Java", 10);
printArrayItem(names, i);
 
// bezne vyhledavani neexistujiciho prvku
i = _.indexOf(names, "BASIC");
printArrayItem(names, i);
 
console.log("---------------------------------------");
 
 
 
console.log("_.lastIndexOf():");
 
// bezne vyhledavani posledniho prvku
i = _.lastIndexOf(names, "Java");
printArrayItem(names, i);
 
// vyhledavani od desateho prvku
i = _.lastIndexOf(names, "Java", 10);
printArrayItem(names, i);
 
// bezne vyhledavani neexistujiciho prvku
i = _.lastIndexOf(names, "BASIC");
printArrayItem(names, i);
 
console.log("---------------------------------------");
 
 
 
/* predikat vracejici true pouze v pripade, ze se v prvku
 * nalezne podretezec odpovidajici regularnimu vyrazu.*/
function predicate(item)
{
    return /.*Java.*/.test(item);
}
 
/* predikat vracejici vzdy hodnotu true */
function constantTrue(item)
{
    return true;
}
 
/* predikat vracejici vzdy hodnotu false */
function constantFalse(item)
{
    return false;
}
 
 
 
console.log("_.findIndex():");
 
i = _.findIndex(names, predicate);
printArrayItem(names, i);
 
i = _.findIndex(names, constantTrue);
printArrayItem(names, i);
 
i = _.findIndex(names, constantFalse);
printArrayItem(names, i);
 
console.log("---------------------------------------");
 
 
 
console.log("_.findLastIndex():");
 
i = _.findLastIndex(names, predicate);
printArrayItem(names, i);
 
i = _.findLastIndex(names, constantTrue);
printArrayItem(names, i);
 
i = _.findLastIndex(names, constantFalse);
printArrayItem(names, i);
 
console.log("---------------------------------------");
 
 
 
// ------------------------------------------------------------
// Finito
// ------------------------------------------------------------

Výsledek běhu třetího demonstračního příkladu může vypadat následovně (konzoli je opět možné zobrazit například přes Firebug apod.):

_.indexOf():
#3 = Java
#14 = Java
#-1 = undefined
---------------------------------------
_.lastIndexOf():
#14 = Java
#3 = Java
#-1 = undefined
---------------------------------------
_.findIndex():
#3 = Java
#0 = C
#-1 = undefined
---------------------------------------
_.findLastIndex():
#14 = Java
#18 = Forth
#-1 = undefined
---------------------------------------

10. Množinové operace s poli

V mnoha programech, v nichž se volají funkce nabízené knihovnou Underscore, se pole používají i jako náhrada za množiny, ostatně většina databázových operací ve skutečnosti vrací množiny či setříděné množiny a nikoli seznamy. Programátoři mají k dispozici trojici funkcí nazvaných union, intersection a difference, s jejichž využitím se implementují operace pro sjednocení množin, průnik množin a rozdíl množin. Nejprve se podívejme na to, jak tyto funkce vypadají a jaké očekávají parametry:

# Funkce Význam
1 _.union(pole1, …) provedení operace sjednocení množin
2 _.intersection(pole1, …) provedení operace průniku množin
3 _.difference(pole1, pole2, …) provedení operace rozdílu množin

Poznámky:

  1. Funkce union a intersection akceptují alespoň jeden parametr typu pole. Pokud je jim předán jediný parametr, logicky se vrátí původní pole. Pokud je těmto funkcím předáno větší množství parametrů (teoreticky libovolný počet), provede se se všemi poli daná operace a vrátí se nové pole.
  2. Naproti tomu funkce difference akceptuje minimálně dva parametry. Taktéž zde záleží na pořadí polí, protože výsledkem této funkce je pole s prvky, které se nachází v prvním poli a současně se nenachází v žádných dalších polích.

Podívejme se na příklady, z nichž bude chování všech tří funkcí patrnější:

_.union([1,2,3], [2,3,4])
[1,2,3,4]
 
_.union([1,2,3], [4,5,6])
[1,2,3,4,5,6]
 
_.intersection([1,2,3], [2,3,4])
[2,3]
 
_.intersection([1,2,3], [4,5,6])
[]
 
_.difference([1,2,3], [2,3,4])
[1]
 
_.difference([1,2,3], [4,5,6])
[1,2,3]
 
_.difference([4,5,6], [1,2,3])
[4,5,6])
 

11. Čtvrtý demonstrační příklad

Množinové operace zmíněné v předchozí kapitole si můžeme vyzkoušet ve čtvrtém (předposledním) demonstračním příkladu. Podívejme se nyní na zdrojový kód tohoto příkladu, který je vlastně ve skutečnosti velmi jednoduchý:

// ------------------------------------------------------------
// Knihovna Underscore.js: demonstracni priklad cislo 4
//                         Mnozinove operace s poli.
// ------------------------------------------------------------
 
 
 
// funkce pro vypis informaci o vybranem poli (ci jinem objektu)
function printArrayInfo(expression) {
    var anArray = eval(expression);
    console.log("---------------------------------");
    console.log(expression);
 
    // zjisteni typu objektu (a pripadne delky pole)
    if (anArray instanceof Array) {
        console.log("type:    array");
        console.log("length:  " + anArray.length);
    }
    // jiny typ objektu, nemame zde jistotu, ze existuje atribut length
    else {
        console.log("type:    " + typeof anArray);
    }
    console.log("content: " + anArray);
}
 
 
 
// pole s prvky 1..10
var array1 = _.range(1, 11);
 
// pole s prvky 6..15
var array2 = _.range(6, 16);
 
// pole s prvky 101..110
var array3 = _.range(101, 111);
 
printArrayInfo("array1");
printArrayInfo("array2");
printArrayInfo("array3");
 
// sjednoceni mnozin
printArrayInfo("_.union(array1, array2)");
printArrayInfo("_.union(array2, array1)");
printArrayInfo("_.union(array1, array3)");
 
printArrayInfo("_.union(array1, array2, array3)");
 
// prunik mnozin se spolecnymi prvky
printArrayInfo("_.intersection(array1, array2)");
printArrayInfo("_.intersection(array2, array1)");
 
// mnoziny nemaji spolecne prvky
printArrayInfo("_.intersection(array1, array3)");
 
// mnoziny nemaji spolecne prvky
printArrayInfo("_.intersection(array1, array2, array3)");
 
// rozdil mnozin (zde samozrejme zalezi na poradi)
printArrayInfo("_.difference(array1, array2)");
printArrayInfo("_.difference(array2, array1)");
printArrayInfo("_.difference(array1, array3)");
printArrayInfo("_.difference(array3, array1)");
 
printArrayInfo("_.difference(array1, array2, array3)");
 
// specialni pripad :)
printArrayInfo("_.difference(array1, array1)");
 
 
 
// ------------------------------------------------------------
// Finito
// ------------------------------------------------------------

Výsledek běhu čtvrtého demonstračního příkladu může vypadat následovně (konzoli je … ale to už víte):

---------------------------------
array1
type:    array
length:  10
content: 1,2,3,4,5,6,7,8,9,10
---------------------------------
array2
type:    array
length:  10
content: 6,7,8,9,10,11,12,13,14,15
---------------------------------
array3
type:    array
length:  10
content: 101,102,103,104,105,106,107,108,109,110
---------------------------------
_.union(array1, array2)
type:    array
length:  15
content: 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
---------------------------------
_.union(array2, array1)
type:    array
length:  15
content: 6,7,8,9,10,11,12,13,14,15,1,2,3,4,5
---------------------------------
_.union(array1, array3)
type:    array
length:  20
content: 1,2,3,4,5,6,7,8,9,10,101,102,103,104,105,106,107,108,109,110
---------------------------------
_.union(array1, array2, array3)
type:    array
length:  25
content: 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,101,102,103,104,105,106,107,108,109,110
---------------------------------
_.intersection(array1, array2)
type:    array
length:  5
content: 6,7,8,9,10
---------------------------------
_.intersection(array2, array1)
type:    array
length:  5
content: 6,7,8,9,10
---------------------------------
_.intersection(array1, array3)
type:    array
length:  0
content:
---------------------------------
_.intersection(array1, array2, array3)
type:    array
length:  0
content:
---------------------------------
_.difference(array1, array2)
type:    array
length:  5
content: 1,2,3,4,5
---------------------------------
_.difference(array2, array1)
type:    array
length:  5
content: 11,12,13,14,15
---------------------------------
_.difference(array1, array3)
type:    array
length:  10
content: 1,2,3,4,5,6,7,8,9,10
---------------------------------
_.difference(array3, array1)
type:    array
length:  10
content: 101,102,103,104,105,106,107,108,109,110
---------------------------------
_.difference(array1, array2, array3)
type:    array
length:  5
content: 1,2,3,4,5
---------------------------------
_.difference(array1, array1)
type:    array
length:  0
content:

12. Zploštění vnořených polí, kombinace prvků z různých polí atd.

V knihovně Underscore nalezneme i mnohé další užitečné funkce používané pro práci s poli, kombinaci prvků z různých polí apod. Jedná se především o funkce nazvané flatten, zip a unzip (pozor na to, že zejména funkce zip má v jiných jazycích či knihovnách někdy poněkud odlišný význam, což se týká například i ClojureScriptu a tudíž i dříve popsané knihovny Wisp). Způsob volání těchto funkcí je vypsán v následující tabulce, pod tabulkou pak následují podrobnější popisy jednotlivých funkcí:

# Funkce Význam
1 _.flatten(pole) zploštění pole (resp. rekurzivně všech pod-polí)
2 _.flatten(pole, true) zploštění pole, ovšem jen v první úrovni
3 _.zip(sekvence polí) ze vstupních polí vytvoří nové pole, viz popis a příklady uvedené níže
4 _.unzip(pole) opak předchozí funkce, ze vstupního pole vytvoří sekvenci polí
5 _.object(pole) (zde uvedeno pouze pro doplnění, bude vysvětleno příště), toto je obdoba funkce zip z ClojureScriptu a knihovny Wisp, protože převádí pole na objekty sloužící i jako mapa)

Funkce flatten pracuje tak, jak její název napovídá: „zplošťuje“ pole, což znamená, že se ve vstupním poli (které zůstává nezměněno) všechny prvky obsahující (i rekurzivně) taktéž pole rozloží na jednotlivé elementy. Druhým argumentem (pokud se mu předá hodnota true) lze zploštění omezit pouze na první úroveň, tj. zploštění se nebude provádět rekurzivně. Podívejme se na příklady, z nichž bude chování této funkce zřejmé:

_.flatten([1, 2, 3, 4, 5]);
[1, 2, 3, 4, 5]
 
_.flatten([[1, 2], [3, 4], 5]);
[1, 2, 3, 4, 5]
 
_.flatten([1, [2, [3, [4, [5]]]]]);
 
[1, 2, 3, 4, 5]
 
_.flatten([1, [2], [3, [[4]]]], true);
[1, 2, 3, [[4]]];

Poněkud složitěji se chová funkce zip, které je možné předat libovolný počet polí o různé délce. Tato funkce vytvoří nové pole obsahující jako své prvky n-tice (což zde není nic jiného než pole). První prvek bude n-ticí (polem) složeným z prvních prvků všech předaných polí, druhý prvek bude n-tice složená ze druhých prvků předaných polí atd. Vstupní pole mohou mít rozdílnou délku, takže jednotlivé n-tice nemusí mít stejný počet prvků. Opět se podívejme na příklady:

// pole s prvky 1..10
var array1 = _.range(1, 11);
 
// pole s prvky "A".."J"
var array2 = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J"];
 
// vytvoření pole dvojic
_.zip(array1, array2)
[[1,A], [2,B], [3,C], [4,D], [5,E], [6,F], [7,G], [8,H], [9,I], [10,J]]
 
// vytvoření pole dvojic
_.zip(array2, array2)
[[A,A], [B,B], [C,C], [D,D], [E,E], [F,F], [G,G], [H,H], [I,I], [J,J]]
 
// vytvoření pole trojic dvojic a posléze dvojic
_.zip(array1, array2, [100,200,300])
[[1,A,100], [2,B,200], [3,C,300], [4,D], [5,E], [6,F], [7,G], [8,H], [9,I], [10,J]]

Poznámka: již několikrát jsme se setkali s tím, že JavaScript a tím pádem i knihovna Underscore umožňuje práci s nepravidelnými 2D maticemi, tj. maticemi, jejichž řádky mají nestejnou délku. To je i případ funkce zip, která v obecném případě právě takové nepravidelné 2D matice vytváří.

13. Pátý demonstrační příklad

Chování funkcí flatten a zip, které jsme si ve stručnosti popsali v předchozí kapitole, si otestujeme v dnešním pátém a současně i posledním demonstračním příkladu jehož zdrojový kód je vypsán pod tímto odstavcem. Povšimněte si, že zde mj. používáme i funkci range:

widgety

// ------------------------------------------------------------
// Knihovna Underscore.js: demonstracni priklad cislo 5
//                         Operace zip a flatten.
// ------------------------------------------------------------
 
 
 
// funkce pro vypis informaci o vybranem poli (ci jinem objektu)
function printArrayInfo(expression) {
    var anArray = eval(expression);
    console.log("---------------------------------");
    console.log(expression);
 
    // zjisteni typu objektu (a pripadne delky pole)
    if (anArray instanceof Array) {
        console.log("type:    array");
        console.log("length:  " + anArray.length);
        // nyni pracujeme s 2D poli, takze si postupne vypiseme jeho radky
        // (console.log totiz vypise i 2D pole jako jeden vektor)
        for (var i=0; i < anArray.length; i++) {
            console.log("row #" + i + " = " + anArray[i]);
        }
    }
    // jiny typ objektu, nemame zde jistotu, ze existuje atribut length
    else {
        console.log("type:    " + typeof anArray);
        console.log("content: " + anArray);
    }
}
 
 
 
// pole s prvky 1..10
var array1 = _.range(1, 11);
 
// pole s prvky "A".."J"
var array2 = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J"];
 
printArrayInfo("array1");
printArrayInfo("array2");
 
 
// kombinace prvku ze dvou poli
printArrayInfo("_.zip(array1, array2)");
printArrayInfo("_.zip(array2, array1)");
 
// kombinace prvku ze stejneho vstupniho pole
printArrayInfo("_.zip(array2, array2)");
 
// i toto je povoleno, posledni pole je kratsi
printArrayInfo("_.zip(array1, array2, [100,200,300])");
 
 
 
// otestovani funkce flatten()
var array3 = [_.range(1, 6), _.range(5,10), _.range(9,15)];
printArrayInfo("array3");
printArrayInfo("_.flatten(array3)");
 
 
 
// ------------------------------------------------------------
// Finito
// ------------------------------------------------------------

Výsledek běhu pátého demonstračního příkladu může vypadat následovně (konzoli je … ale to už víte):

---------------------------------
array1
type:    array
length:  10
row #0 = 1
row #1 = 2
row #2 = 3
row #3 = 4
row #4 = 5
row #5 = 6
row #6 = 7
row #7 = 8
row #8 = 9
row #9 = 10
---------------------------------
array2
type:    array
length:  10
row #0 = A
row #1 = B
row #2 = C
row #3 = D
row #4 = E
row #5 = F
row #6 = G
row #7 = H
row #8 = I
row #9 = J
---------------------------------
_.zip(array1, array2)
type:    array
length:  10
row #0 = 1,A
row #1 = 2,B
row #2 = 3,C
row #3 = 4,D
row #4 = 5,E
row #5 = 6,F
row #6 = 7,G
row #7 = 8,H
row #8 = 9,I
row #9 = 10,J
---------------------------------
_.zip(array2, array1)
type:    array
length:  10
row #0 = A,1
row #1 = B,2
row #2 = C,3
row #3 = D,4
row #4 = E,5
row #5 = F,6
row #6 = G,7
row #7 = H,8
row #8 = I,9
row #9 = J,10
---------------------------------
_.zip(array2, array2)
type:    array
length:  10
row #0 = A,A
row #1 = B,B
row #2 = C,C
row #3 = D,D
row #4 = E,E
row #5 = F,F
row #6 = G,G
row #7 = H,H
row #8 = I,I
row #9 = J,J
---------------------------------
_.zip(array1, array2, [100,200,300])
type:    array
length:  10
row #0 = 1,A,100
row #1 = 2,B,200
row #2 = 3,C,300
row #3 = 4,D,
row #4 = 5,E,
row #5 = 6,F,
row #6 = 7,G,
row #7 = 8,H,
row #8 = 9,I,
row #9 = 10,J,
---------------------------------
array3
type:    array
length:  3
row #0 = 1,2,3,4,5
row #1 = 5,6,7,8,9
row #2 = 9,10,11,12,13,14
---------------------------------
_.flatten(array3)
type:    array
length:  16
row #0 = 1
row #1 = 2
row #2 = 3
row #3 = 4
row #4 = 5
row #5 = 5
row #6 = 6
row #7 = 7
row #8 = 8
row #9 = 9
row #10 = 9
row #11 = 10
row #12 = 11
row #13 = 12
row #14 = 13
row #15 = 14

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

Všech pět demonstračních příkladů, které jsme si v dnešním článku popsali, bylo uloženo do Git repositáře dostupného na adrese https://github.com/tisnik/pre­sentations. V tabulce zobrazené pod tímto odstavcem naleznete na zdrojové kódy všech pěti demonstračních příkladů přímé odkazy (navíc je doplněn i odkaz na aktuálně použitou variantu knihovny Underscore):

15. Odkazy na Internetu

  1. Stránky knihovny Underscore s popisem všech funkcí
    http://underscorejs.org/
  2. Minifikovaná verze knihovny Underscore
    http://underscorejs.org/underscore-min.js
  3. Functional Programming (Wikipedia)
    https://en.wikipedia.org/wi­ki/Functional_programming
  4. An Introduction to Functional Programming in JavaScript
    http://www.srirangan.net/2011–12-functional-programming-in-javascript
  5. Getting Cozy With Underscore.js
    http://code.tutsplus.com/tu­torials/getting-cozy-with-underscorejs–net-24581
  6. Mori na GitHubu
    https://github.com/swannodette/mori
  7. Mori: popis API (dokumentace)
    http://swannodette.github.io/mori/
  8. Mori: Benchmarking
    https://github.com/swanno­dette/mori/wiki/Benchmarking
  9. Functional data structures in JavaScript with Mori
    http://sitr.us/2013/11/04/functional-data-structures.html
  10. Immutable.js
    https://facebook.github.io/immutable-js/
  11. Persistent data structure
    https://en.wikipedia.org/wi­ki/Persistent_data_structu­re
  12. Understanding Clojure's Persistent Vectors, pt. 1
    http://hypirion.com/musin­gs/understanding-persistent-vector-pt-1
  13. Hash array mapped trie (Wikipedia)
    https://en.wikipedia.org/wi­ki/Hash_array_mapped_trie
  14. Java theory and practice: To mutate or not to mutate?
    http://www.ibm.com/develo­perworks/java/library/j-jtp02183/index.html
  15. Efficient persistent (immutable) data structures
    https://persistent.codeplex.com/
  16. Netscape Enterprise Server (Wikipedia)
    https://en.wikipedia.org/wi­ki/Netscape_Enterprise_Ser­ver
  17. SSJS Reference Guide (Server-Side JavaScript)
    http://docs.oracle.com/cd/E19957–01/816–6410–10/816–6410–10.pdf
  18. Atom: moderní textový editor
    http://www.root.cz/clanky/atom-moderni-textovy-editor/
  19. Atom: moderní textový editor (dokončení)
    http://www.root.cz/clanky/atom-moderni-textovy-editor-dokonceni/
  20. Propojení světa LISPu se světem JavaScriptu s využitím transpřekladače Wisp
    http://www.root.cz/clanky/propojeni-sveta-lispu-se-svetem-javascriptu-s-vyuzitim-transprekladace-wisp/
  21. Propojení světa LISPu se světem JavaScriptu s využitím transpřekladače Wisp (2.část)
    http://www.root.cz/clanky/propojeni-sveta-lispu-se-svetem-javascriptu-s-vyuzitim-transprekladace-wisp-2-cast/
  22. Wisp na GitHubu
    https://github.com/Gozala/wisp
  23. Wisp playground
    http://www.jeditoolkit.com/try-wisp/
  24. REPL v prohlížeči
    http://www.jeditoolkit.com/in­teractivate-wisp/
  25. Minification (programming)
    https://en.wikipedia.org/wi­ki/Minification_(programmin­g)
  26. Clojure Macro Tutorial (Part I, Getting the Compiler to Write Your Code For You)
    http://www.learningclojure­.com/2010/09/clojure-macro-tutorial-part-i-getting.html
  27. Clojure Macro Tutorial (Part II: The Compiler Strikes Back)
    http://www.learningclojure­.com/2010/09/clojure-macro-tutorial-part-ii-compiler.html
  28. Clojure Macro Tutorial (Part III: Syntax Quote)
    http://www.learningclojure­.com/2010/09/clojure-macro-tutorial-part-ii-syntax.html
  29. Tech behind Tech: Clojure Macros Simplified
    http://techbehindtech.com/2010/09/28/clo­jure-macros-simplified/
  30. Fatvat – Exploring functional programming: Clojure Macros
    http://www.fatvat.co.uk/2009/02/clo­jure-macros.html
  31. Eulerovo číslo
    http://cs.wikipedia.org/wi­ki/Eulerovo_??slo
  32. List comprehension
    http://en.wikipedia.org/wi­ki/List_comprehension
  33. List Comprehensions in Clojure
    http://asymmetrical-view.com/2008/11/18/list-comprehensions-in-clojure.html
  34. Clojure Programming Concepts: List Comprehension
    http://en.wikibooks.org/wi­ki/Clojure_Programming/Con­cepts#List_Comprehension
  35. Clojure core API: for macro
    http://clojure.github.com/clo­jure/clojure.core-api.html#clojure.core/for
  36. cirrus machina – The Clojure for macro
    http://www.cirrusmachina.com/blog/com­ment/the-clojure-for-macro/
  37. Clojure.org: Clojure home page
    http://clojure.org/downloads
  38. Clojure.org: Vars and the Global Environment
    http://clojure.org/Vars
  39. Clojure.org: Refs and Transactions
    http://clojure.org/Refs
  40. Clojure.org: Atoms
    http://clojure.org/Atoms
  41. Clojure.org: Agents as Asynchronous Actions
    http://clojure.org/agents
  42. A Couple of Clojure Agent Examples
    http://lethain.com/a-couple-of-clojure-agent-examples/
  43. Clojure – Functional Programming for the JVM
    http://java.ociweb.com/mar­k/clojure/article.html
  44. Clojure quick reference
    http://faustus.webatu.com/clj-quick-ref.html
  45. 4Clojure
    http://www.4clojure.com/
  46. ClojureDoc
    http://clojuredocs.org/
  47. Clojure (Wikipedia EN)
    http://en.wikipedia.org/wiki/Clojure
  48. Clojure (Wikipedia CS)
    http://cs.wikipedia.org/wiki/Clojure
  49. Riastradh's Lisp Style Rules
    http://mumble.net/~campbe­ll/scheme/style.txt
  50. Dynamic Languages Strike Back
    http://steve-yegge.blogspot.cz/2008/05/dynamic-languages-strike-back.html
  51. Scripting: Higher Level Programming for the 21st Century
    http://www.tcl.tk/doc/scripting.html
  52. Java Virtual Machine Support for Non-Java Languages
    http://docs.oracle.com/ja­vase/7/docs/technotes/gui­des/vm/multiple-language-support.html
  53. The Lua VM, on the Web
    https://kripken.github.io/lu­a.vm.js/lua.vm.js.html
  54. Lua.vm.js REPL
    https://kripken.github.io/lu­a.vm.js/repl.html
  55. lua2js
    https://www.npmjs.com/package/lua2js
  56. lua2js na GitHubu
    https://github.com/basicer/lua2js-dist
  57. Seriál o programovacím jazyku Lua
    http://www.root.cz/serialy/pro­gramovaci-jazyk-lua/
  58. Source-to-source compiler
    https://en.wikipedia.org/wiki/Source-to-source_compiler
  59. JavaScript is Assembly Language for the Web: Sematic Markup is Dead! Clean vs. Machine-coded HTML
    http://www.hanselman.com/blog/Ja­vaScriptIsAssemblyLanguage­ForTheWebSematicMarkupIsDe­adCleanVsMachinecodedHTML­.aspx
  60. JavaScript is Web Assembly Language and that's OK.
    http://www.hanselman.com/blog/Ja­vaScriptIsWebAssemblyLangu­ageAndThatsOK.aspx
  61. Dart
    https://www.dartlang.org/
  62. CoffeeScript
    http://coffeescript.org/
  63. TypeScript
    http://www.typescriptlang.org/
  64. Lua (programming language)
    http://en.wikipedia.org/wi­ki/Lua_(programming_langu­age)
  65. Static single assignment form (SSA)
    http://en.wikipedia.org/wi­ki/Static_single_assignmen­t_form
  66. LuaJIT 2.0 SSA IRhttp://wiki.luajit.org/SSA-IR-2.0
  67. The LuaJIT Project
    http://luajit.org/index.html
  68. LuaJIT FAQ
    http://luajit.org/faq.html
  69. LuaJIT Performance Comparison
    http://luajit.org/performance.html
  70. LuaJIT 2.0 intellectual property disclosure and research opportunities
    http://article.gmane.org/gma­ne.comp.lang.lua.general/58908
  71. LuaJIT Wiki
    http://wiki.luajit.org/Home
  72. LuaJIT 2.0 Bytecode Instructions
    http://wiki.luajit.org/Bytecode-2.0
  73. Programming in Lua (first edition)
    http://www.lua.org/pil/contents.html
  74. Lua 5.2 sources
    http://www.lua.org/source/5.2/
  75. Tcl Plugin Version 3
    http://www.tcl.tk/software/plugin/
  76. JavaScript: The Web Assembly Language?
    http://www.informit.com/ar­ticles/article.aspx?p=1856657
  77. asm.js
    http://asmjs.org/
  78. List of languages that compile to JS
    https://github.com/jashke­nas/coffeescript/wiki/List-of-languages-that-compile-to-JS
  79. REPL
    https://en.wikipedia.org/wi­ki/Read%E2%80%93eval%E2%80%93prin­t_loop
  80. The LLVM Compiler Infrastructure
    http://llvm.org/ProjectsWithLLVM/
  81. clang: a C language family frontend for LLVM
    http://clang.llvm.org/
  82. emscripten
    http://kripken.github.io/emscripten-site/
  83. LLVM Backend („Fastcomp“)
    http://kripken.github.io/emscripten-site/docs/building_from_source/LLVM-Backend.html#llvm-backend
  84. Emscripten – Fastcomp na GitHubu
    https://github.com/kripken/emscripten-fastcomp
  85. Clang (pro Emscripten) na GitHubu
    https://github.com/kripken/emscripten-fastcomp-clang
  86. Why not use JavaScript?
    https://ckknight.github.i­o/gorillascript/
Našli jste v článku chybu?
Lupa.cz: Proč jsou firemní počítače pomalé?

Proč jsou firemní počítače pomalé?

120na80.cz: Pálení žáhy: která jídla ne a co nás uzdraví?

Pálení žáhy: která jídla ne a co nás uzdraví?

Vitalia.cz: Tesco nabízí desítky tun jídla zdarma

Tesco nabízí desítky tun jídla zdarma

Lupa.cz: Jak levné procesory změnily svět?

Jak levné procesory změnily svět?

Lupa.cz: Cimrman má hry na YouTube i vlastní doodle

Cimrman má hry na YouTube i vlastní doodle

120na80.cz: Co je padesátkrát sladší než cukr?

Co je padesátkrát sladší než cukr?

Podnikatel.cz: Byla finanční manažerka, teď cvičí jógu

Byla finanční manažerka, teď cvičí jógu

Podnikatel.cz: Babišovi se nedá věřit, stěžovali si hospodští

Babišovi se nedá věřit, stěžovali si hospodští

DigiZone.cz: Samsung EVO-S: novinka pro Skylink

Samsung EVO-S: novinka pro Skylink

DigiZone.cz: Funbox 4K v DVB-T2 má ostrý provoz

Funbox 4K v DVB-T2 má ostrý provoz

DigiZone.cz: Světový pohár v přímém přenosu na ČT

Světový pohár v přímém přenosu na ČT

DigiZone.cz: Ginx TV: pořad o počítačových hráčích

Ginx TV: pořad o počítačových hráčích

Lupa.cz: Jak se prodává firma za miliardu?

Jak se prodává firma za miliardu?

DigiZone.cz: Parlamentní listy: kde končí PR...

Parlamentní listy: kde končí PR...

DigiZone.cz: Technisat připravuje trojici DAB

Technisat připravuje trojici DAB

Vitalia.cz: Muž, který miluje příliš. Ženám neimponuje

Muž, který miluje příliš. Ženám neimponuje

Vitalia.cz: 5 chyb, které děláme při skladování potravin

5 chyb, které děláme při skladování potravin

Podnikatel.cz: Instalatér, malíř a elektrikář. "Vymřou"?

Instalatér, malíř a elektrikář. "Vymřou"?

DigiZone.cz: Digi Slovakia zařazuje stanice SPI

Digi Slovakia zařazuje stanice SPI

Lupa.cz: Patička e-mailu závazná jako vlastnoruční podpis?

Patička e-mailu závazná jako vlastnoruční podpis?