Clojure aneb jazyk umožňující tvorbu bezpečných vícevláknových aplikací pro JVM (4.část - kolekce, sekvence a lazy sekvence)

3. 7. 2012
Doba čtení: 20 minut

Sdílet

V dnešní části seriálu o programovacím jazyce Java i o vlastnostech JVM budeme, stejně jako v předchozích třech částech, pokračovat v popisu programovacího jazyka Clojure. Dnešním ústředním tématem bude práce s kolekcemi a se sekvencemi, protože především sekvence tvoří velmi elegantní a důležitou součást Clojure.

Obsah

1. Predikáty a testy na ekvivalenci

2. Co možná mělo zůstat utajeno aneb programujeme imperativně ve funkcionálním jazyce

3. Makro dotimes

4. Speciální forma do a makro while

5. Základní operace s kolekcemi

6. Demonstrační příklady – práce s kolekcemi

7. Sekvence, lazy sekvence a funkce range

8. Funkce repeat a map

9. Odkazy na Internetu

1. Predikáty a testy na ekvivalenci

V dnešní části seriálu o programovacím jazyce Java i o vlastnostech virtuálního stroje tohoto jazyka si popíšeme způsob práce s kolekcemi a taktéž se budeme zabývat takzvanými sekvencemi a lazy sekvencemi, které tvoří velmi důležitou součást programovacího jazyka Clojure. Ještě předtím si však popíšeme takzvané predikáty, tj. funkce vracející hodnotu true či false v závislosti na hodnotě a/nebo typu parametru, jenž je predikátu předán. V některých variantách programovacího jazyka LISP bylo zvykem za jméno predikátu dávat znak „p“, takže například funkce/predikát pro test na nulovou hodnotu (velmi často používaný predikát) měl název zerop. V programovacím jazyku Clojure se namísto znaku „p“ používá otazník, protože i otazník může být součástí názvu identifikátoru. V následující tabulce jsou vypsány základní predikáty, z nichž některé později využijeme v demonstračních příkladech (což je ostatně jeden z hlavních důvodů, proč jsou zde predikáty vůbec uváděny):

# Predikát Význam
1 nil? test, zda je předaná hodnota rovna literálu nil
2 true? test, zda je předaná hodnota rovna literálu true
3 false? test, zda je předaná hodnota rovna literálu false
4 number? test na číslo (libovolného typu)
5 integer? test na celé číslo
6 ratio? test na zlomek (nikoli na obecné desetinné číslo)
7 float? test na desetinné číslo
8 decimal? test na hodnotu typu BigDecimal
9 even? test na sudou hodnotu
10 odd? test na lichou hodnotu
11 pos? test na kladnou hodnotu
12 neg? test na zápornou hodnotu
13 zero? test na nulu
14 keyword? test, zda je předaná hodnota typu klíčové heslo
15 symbol? test, zda je předaná hodnota typu symbol
16 char? test, zda je předaná hodnota typu char
17 string? test, zda je předaná hodnota typu řetězec
18 seq? test, zda je předaná hodnota typu sekvence

Použití predikátů je velmi jednoduché, takže si ukážeme jen několik jednořádkových demonstračních příkladů.

Na rozdíl od jazyka LISP není literál nil totožný s prázdným seznamem:

user=> (nil? '())
false

nil je vlastně singleton/jedináček:

user=> (nil? nil)
true

Dokonce ani funkce rest (aka cdr) nevrací nil:

user=> (nil? (rest '()))
false

Ukázka použití predikátu true?:

user=> (true? (and true false))
false

Různé testy na číselné hodnoty:

user=> (number? 42)
true
user=> (integer? 42)
true
user=> (integer? 42.0)
false
user=> (ratio? 42)
false

84/2 vlastně není zlomek, protože dojde k jeho vyhodnocení (zjednodušení) na 42 a teprve tato hodnota je testována predikátem ratio?:

user=> (ratio? 84/2)
false

Ovšem 4/3 už nelze nijak zjednodušit:

user=> (ratio? 4/3)
true
user=> (even? 42)
true
user=> (symbol? 42)
false
user=> (def cislo 42)
#'user/cislo

cislo se vyhodnotí na 42:

user=> (symbol? cislo)
false

Zakážeme vyhodnocení pomocí apostrofu:

user=> (symbol? 'cislo)
true

2. Co možná mělo zůstat utajeno aneb programujeme imperativně ve funkcionálním jazyce

Ještě než se pustíme do popisu vlastnosti kolekcí a sekvencí, vraťme se na chvíli k předchozí části tohoto seriálu, konkrétně ke kapitolám 7 a 8, v nichž jsme si ukázali způsob zápisu rekurzivních algoritmů a taktéž algoritmů využívajících tail rekurzi, která je překladačem jazyka Clojure převedena na prostý skok. Na závěr osmé kapitoly jsme si taktéž řekli, že i přesto, že je rekurze velmi užitečný nástroj, není v Clojure používána v takové míře, jak by se možná mohlo z různých „čistě funkcionálních“ demonstračních příkladů zdát. Namísto rekurze se velmi často používají právě sekvence (viz další text), ovšem Clojure dokáže používat i postupy známé z imperativních jazyků.

Z hlediska čistoty programovacího jazyka Clojure by možná neměly následující speciální formy a makra vůbec existovat, nicméně vzhledem k tomu, že si někdo dal tu práci s jejich vytvořením, můžeme se podívat na to, jak je možné některé algoritmy zapisovat „imperativně“. Na tomto místě je však nutné varovat před tím, že použitím imperativního způsobu zápisu algoritmů se například zhoršují možnosti překladače programovacího jazyka Clojure v optimalizaci překládaných programů tak, aby mohly běžet paralelně.

3. Makro dotimes

V některých programech může být poměrně užitečné makro nazvané jednoduše a přitom příhodně dotimes, které dokáže nějaký výraz (formu) opakovat n krát. Přitom toto makro může v každé iteraci (opakování) nastavit zvolenou lokální proměnnou na aktuální hodnotu počitadla, přičemž se hodnota počitadla v první iteraci vždy nastavuje na nulu a v poslední iteraci dosahuje zadaného počtu opakování-1. Vzdáleně tedy můžeme toto makro považovat za ekvivalent programové smyčky for i in range(n): v programovacím jazyku Python či ekvivalent k počítané smyčce for (int i = 0; i<n; i++) známé z céčka (zde bez možnosti mít lokální proměnnou jako počitadlo), C++, Javy atd. Vzhledem k tomu, že se předpokládá, že forma – tělo smyčky – předaná makru dotimes bude mít nějaký vedlejší efekt, nejedná se sice o čistě funkcionální přístup, nicméně makro dotimes může být skutečně velmi užitečné.

V jednoduchém demonstračním příkladu, který si ukážeme, se na standardní výstup vypisuje převrácená hodnota celých čísel od 0 do 9. Vedlejším efektem je v tomto případě samotný výpis na standardní výstup:

(dotimes [i 10] (println (/ 1.0 i)))
Infinity
1.0
0.5
0.3333333333333333
0.25
0.2
0.16666666666666666
0.14285714285714285
0.125
0.1111111111111111
nil

Poznámka: poslední vypsané nil je návratovou hodnotou samotného makra dotimes, nikoli výsledek poslední iterace)

Podívejme se nyní na poněkud složitější příklad, který by se v imperativních programovacích jazycích většinou řešil s využitím dvojice do sebe vnořených počítaných programových smyček. Mějme za úkol vypsat tabulku malé násobilky, tj. všechny výsledky vzniklé vynásobením dvojic celých čísel od 1 do 10. Tento algoritmus je možné velmi snadno realizovat právě s využitím makra dotimes, například následujícím one-linerem:

(dotimes [i 10] (dotimes [j 10] (print (* (+ i 1) (+ j 1)) "\t")) (println))

Malou optimalizací v tomto zápisu by byla náhrada výrazů (+ i 1) za volání funkce (inc i) s prakticky shodným významem (alespoň pro čísla typu integer). Mimochodem: jméno funkce inc může evokovat to, že zvyšuje hodnotu svého parametru, což však ve skutečnosti ani není možné, neboť se jedná o běžnou funkci a nikoli o makro či o speciální formu:

(dotimes [i 10] (dotimes [j 10] (print (* (inc i) (inc j)) "\t")) (println))

Pro větší přehlednost si můžeme výše uvedený one-liner přepsat na správně odsazený program, z něhož je patrné, že se skutečně jedná o ekvivalent dvou do sebe zanořených programových smyček:

(dotimes [i 10]
    (dotimes [j 10]
        (print (* (inc i) (inc j)) "\t"))
    (println))

A zde je již výsledek práce tohoto programu (poslední nil je opět návratovou hodnotou makra dotimes):

1     2     3     4     5     6     7     8     9     10
2     4     6     8     10    12    14    16    18    20
3     6     9     12    15    18    21    24    27    30
4     8     12    16    20    24    28    32    36    40
5     10    15    20    25    30    35    40    45    50
6     12    18    24    30    36    42    48    54    60
7     14    21    28    35    42    49    56    63    70
8     16    24    32    40    48    56    64    72    80
9     18    27    36    45    54    63    72    81    90
10    20    30    40    50    60    70    80    90    100
nil

4. Speciální forma do a makro while

Pokračujme v popisu „imperativních“ konstrukcí, které byly z více či méně racionálních důvodů přidány do funkcionálního programovacího jazyka Clojure. Kromě makra dotimes popsaného v předchozí kapitole se ještě relativně často využívá speciální forma nazvaná do. Této speciální formě lze předat libovolný počet forem/výrazů, které jsou postupně vyhodnoceny (spuštěny) v takovém pořadí, v jakém jsou zapsány. Návratovou hodnotou speciální formy do je návratová hodnota posledního vyhodnoceného výrazu. Vzhledem k tomu, že návratové hodnoty všech předchozích výrazů jsou ztraceny, předpokládá se, že výrazy vyhodnocované v do budou mít vedlejší efekt, jinak by je nemělo smysl ani vyhodnocovat; samozřejmě až na výraz poslední. Opět se podívejme na několik jednoduchých demonstračních příkladů:

Spuštění speciální formy do bez předání dalších forem/výrazů pouze vede k vrácení literálu nil:

user=> (do)
nil

Pokud je speciální formě do předán jediný výraz, je vyhodnocen a jeho návratová hodnota je současně i návratovou hodnotou do (jinými slovy vlastně do nemá v tomto případě žádný zvláštní význam):

user=> (do (+ 1 2))
3

Hodnota prvního výrazu se ztratí a vrátí se hodnota výrazu druhého:

user=> (do (+ 1 2) (+ 3 4))
7

Podobné předchozímu příkladu: ztratí se hodnota dvou vyhodnocených výrazů, třetí výraz má vedlejší efekt a současně se vrátí jeho návratová hodnota, kterou je nil (viz (doc println) či (doc print)):

user=> (do (+ 1 2) (+ 3 4) (println "konec"))
konec
nil

Vzhledem k tomu, že do vyhodnocuje výrazy v takovém pořadí, jak jsou předány, lze se spolehnout i na správné pořadí tisku na standardní výstup (což je vedlejším výrazů/forem efektem):

user=> (do (println "V Ceskych Budejovicich") (println "by chtel") (println "zit kazdy"))
V Ceskych Budejovicich
by chtel
zit kazdy
nil

Speciální forma do nemá sama o sobě moc velký význam, ovšem často se používá společně s makrem while. Asi správně uhodnete, že toto makro iterativně spouští (či lépe řečeno vyhodnocuje) nějaké tělo smyčky, a to tak dlouho dokud platí, že výraz zapsaný za while se vyhodnotí na pravdivostní hodnotu true. Povšimněte si, že se opět tiše předpokládá, že tělo smyčky bude mít nějaký vedlejší efekt jenž způsobí změnu hodnoty výrazu smyčky while. Typickým vedlejším efektem je nastavení hodnoty nějaké lokální či globální proměnné.

Speciální forma do se v těle smyčky používá proto, aby mohlo tělo obsahovat více výrazů, podobně jako tomu bylo u makra dotimes. Příklad smyčky využívající globální proměnnou:

(def a 10)
(while (> a 0) (do (println a) (def a (- a 1))))
10
9
8
7
6
5
4
3
2
1
nil

Pro větší přehlednost lze odečítání provést i s využitím funkce dec a test na kladnou hodnotu provádět predikátem pos?:

(def a 10)
(while (pos? a) (do (println a) (def a (dec a))))
10
9
8
7
6
5
4
3
2
1
nil

A pro ještě větší přehlednost převést one-liner na správně odsazený program:

(def a 10)
(while (pos? a)
    (do (println a)
        (def a (dec a))))
10
9
8
7
6
5
4
3
2
1
nil

Poznámka: opět považuji za vhodné upozornit na to, že idiomy podobné těm uvedeným v této kapitole sice mohou být při přestupu z imperativních programovacích jazyků na Clojure užitečné, ale přináší celou řadu problémů, kterým se lze vyhnout.

5. Základní operace s kolekcemi

Konečně se dostáváme k hlavnímu tématu tohoto článku – k popisu kolekcí a sekvencí. Základní informace o kolekcích jsme si již řekli v předchozích dílech tohoto seriálu: z pohledu jazyka Clojure se jedná o seznamy (list), vektory (vector), množiny (set) a mapy (map). Všechny čtyři typy kolekcí mají jednu společnou vlastnost – jsou totiž nemodifikovatelné, tj. je zaručeno, že se obsah kolekcí nebude při běhu programu nijak měnit, což zjednodušuje přístup k prvkům kolekcí ve chvíli, kdy program běží ve více vláknech. Naproti tomu například pole a kolekce používané v Javě jsou obecně modifikovatelné, takže pro přístup ke kolekcím ve vícevláknových programech je nutné použít některý z dostupných synchronizačních mechanismů, což je z implementačního hlediska poměrně složité, nehledě na to, že při špatném návrhu programu může dojít k dead locku a dalším pro ladění velmi „příjemným“ stavům výsledného programu.

Kolekce jsou v programovacím jazyku Clojure důležité i z jiného důvodu – ve standardní knihovně tohoto jazyka se totiž nachází velké množství funkcí a maker pro práci s kolekcemi. Tvůrci LISPu a z něho odvozeného jazyka Clojure totiž zastávají názor, že je lepší používat relativně malé množství datových typů a mít pro tyto typy k dispozici velké množství obecných funkcí, které je možné vzájemně kombinovat. Naproti tomu se v klasickém OOP spíše upřednostňuje mít větší počet specializovaných datových typů (tříd) s relativně malým množstvím specializovaných funkcí aplikovatelných na tyto typy (metody). Obecně nelze říci, který přístup je lepší, protože záleží na povaze řešené úlohy. Vraťme se však k jazyku Clojure a k jeho kolekcím. V následující tabulce jsou vypsány některé funkce, které lze při práci s kolekcemi použít. Povšimněte si, že žádná z těchto funkcí nemění původní kolekci, maximálně vrátí jako svůj výsledek kolekci novou, která ve většině případů používá stejné prvky, jako kolekce původní (tím se zamezuje zbytečným kopiím v paměti).

# Funkce Význam funkce
1 count vrátí počet prvků v kolekci
2 empty? (s otazníkem na konci) vrátí true v případě, že je kolekce prázdná
3 empty (bez otazníku) vrátí prázdnou kolekci stejného typu
4 not-empty pokud není parametrem prázdná kolekce, vrátí se tato kolekce, jinak se vrátí nil
5 distinct? vrací true, pokud se všechny prvky kolekce od sebe liší
6 sequential? vrací true pro vektory, seznamy a sekvence
7 associative? vrací true pro asociativní typy: vektory (klíčem jsou celá čísla), mapy a struktury
8 cons vrátí novou kolekci s přidaným prvkem (odkaz jazyka LISP)
9 pop vrátí kolekci bez prvního prvku (seznamy) nebo bez prvku posledního (vektory)
10 peek vrátí první prvek (seznam), popř. poslední prvek (vektor)
11 nth získání n-tého prvku kolekce
12 first první prvek kolekce
13 rest kolekce bez prvního prvku

6. Demonstrační příklady – práce s kolekcemi

Ukažme si nyní několik demonstračních příkladů, na nichž je ukázán způsob práce s kolekcemi. Pro jednoduchost si prozatím ukážeme práci se seznamy a vektory.

Nejprve vytvoříme nový seznam a navážeme ho na symbol seznam:

user=> (def seznam '(1 2 3 4 5 6))
#'user/seznam

Taktéž vytvoříme nový vektor, který je navázán na symbol vektor:

user=> (def vektor ['a' 'b' 'c' 'd' 'e' 'f'])
#'user/vektor

Test funkce count je velmi jednoduchý:

user=> (count seznam)
6

Její chování je shodné jak pro seznamy, tak i pro vektory:

user=> (count vektor)
6

Totéž platí pro funkci-predikát empty?:

user=> (empty? seznam)
false

Ta se taktéž chová stejně pro seznamy i pro vektory:

user=> (empty? vektor)
false

Zajímavá je funkce pop, která vrátí nový seznam bez prvního prvku:

user=> (pop seznam)
(2 3 4 5 6)

Ovšem v případě vektorů se vrátí nový vektor bez prvku posledního:

user=> (pop vektor)
[a' b' c' d' e']

I chování funkce peek je odlišné u seznamů a vektorů:

user=> (peek seznam)
1
user=> (peek vektor)
f'

Funkce first odpovídá funkci car z klasického LISPu:

user=> (first seznam)
1
user=> (first vektor)
a'

Funkce rest odpovídá funkci cdr z klasického LISPu:

user=> (rest seznam)
(2 3 4 5 6)
user=> (rest vektor)
(b' c' d' e' f')

Funkce nth vrací n-tý prvek, což však není u seznamů příliš optimální operace, protože má obecně složitost O(n):

user=> (nth seznam 4)
5

U vektorů je složitost funkce nth konstantní, tj. O(1):

user=> (nth vektor 4)
e'

S využitím funkce cons lze vytvářet nové seznamy:

user=> (cons 1 seznam)
(1 1 2 3 4 5 6)

Popř. i nové vektory:

user=> (cons 1 vektor)
(1 a' b' c' d' e' f')

Zajímavá věc se stane, pokud se přidá celá kolekce do jiné kolekce:

user=> (cons seznam vektor)
((1 2 3 4 5 6) a' b' c' d' e' f')
user=> (cons vektor seznam)
([a' b' c' d' e' f'] 1 2 3 4 5 6)

7. Sekvence, lazy sekvence a funkce range

S kolekcemi poměrně úzce souvisí i takzvané sekvence. Tímto termínem se v programovacím jazyce Clojure označuje programové rozhraní, které svými základními možnostmi zhruba odpovídá iterátorům známým z programovacího jazyka Java. V Clojure existuje velké množství funkcí, které dokážou pracovat se sekvencemi, ať již se jedná o běžné sekvence (jejichž prvky jsou přímo uloženy v paměti), nebo takzvané líné sekvence (lazy sekvence), které nové prvky vytváří či zjišťují až při konkrétním přístupu na tyto prvky. Mezi tyto funkce patří například sort, sort-by, reverse či flatten. Díky tomu, že všechny kolekce (viz předchozí dvě kapitoly) jsou současně i sekvencemi, lze tyto funkce aplikovat i na kolekce, ovšem ve skutečnosti jsou sekvencemi i další typy objektů – I/O proudy (je možná škoda, že se tímto směrem nevyvinulo API Javy, které je zrovna v případě I/O operací dosti složité), řetězce (což jsou sekvence znaků) atd.

Naprostý základ pro práci se sekvencemi tvoří trojice funkcí nazvaných first, rest a next. Funkce first vrací první prvek v sekvenci, popř.  speciální hodnotu nil v případě, že je sekvence prázdná. Funkce restnext vrací zbylé prvky v sekvenci, ovšem liší se tím, jaká hodnota se vrátí ve chvíli, kdy již v sekvenci nezbyly žádné prvky (kromě prvního). V tomto případě vrátí rest prázdnou sekvenci (například prázdný seznam), zatímco funkce next vrátí hodnotu nil. U běžných sekvencí, například seznamů, jsou tyto funkce implementovány přímočaře, ovšem v případě lazy sekvencí se prvky vrácené pomocí funkce first vyhodnocují až za běhu, například pomocí nějaké generátorové funkce. Tímto způsobem je možné pracovat i s nekonečnými sekvencemi, u nichž už z principu nelze dopředu znát celkový počet prvků atd.

Velmi dobrým příkladem lazy sekvence je funkce range, která dokonce existuje v několika podobách, jež se od sebe z hlediska programátora-uživatele liší především různým počtem parametrů. Pokud se této funkci nepředá žádný parametr, vrátí funkce range sekvenci celých čísel od nuly do nekonečna. Zde je patrné, proč se musí jednat o lazy sekvenci – nekonečnou řadu celých čísel by samozřejmě v případě normální sekvence nebylo možné uložit do operační paměti. Pokud se funkci range předá pouze jediný parametr (kterým musí být celé číslo – je kontrolováno v runtime), je vrácena sekvence celých čísel od 0 do zadané hodnoty-1. Opět se jedná o nefalšovanou lazy sekvenci, takže se nemusíte bát používat i velké n. Dále již následují v podstatě jen kosmetické úpravy – volání funkce range se dvěma parametry m, n vytvoří sekvenci celých čísel od m do n-1 a pokud je použit ještě třetí parametr, určuje se jím krok, který může být i záporný. Vše si ukážeme na demonstračních příkladech:

Sekvence od 0 do nekonečna – raději nespouštět, protože se sekvence musí vyhodnotit ve smyčce REPL:

user=> (range)

Sekvence od 0 do devíti (asi nejjednodušší příklad reálného použití funkce range):

user=> (range 10)
(0 1 2 3 4 5 6 7 8 9)

Nulová hodnota je tolerována:

user=> (range 0)
()

Záporná hodnota je taktéž tolerována:

user=> (range -10)
()

Vrací lazy sekvenci od 10 do 19:

user=> (range 10 20)
(10 11 12 13 14 15 16 17 18 19)

Podobný příklad, ale s krokem 2:

user=> (range 10 20 2)
(10 12 14 16 18)

Lze použít i záporný krok:

user=> (range 20 10 -1)
(20 19 18 17 16 15 14 13 12 11)

…různý od 1:

user=> (range 20 10 -2)
(20 18 16 14 12)

8. Funkce repeat a map

Kromě funkce range popsané v závěru předchozí kapitoly je další užitečnou funkcí i funkce nazvaná repeat, která slouží k vytvoření lazy sekvence a zadané délce. Pokud je funkci repeat předán jen jeden parametr, vytváří nekonečnou lazy sekvenci, kde se v každé iteraci vrátí tento parametr (nemusí se ani jednat o novou instanci, protože parametr je samozřejmě neměnitelný):

(repeat x)
; opakuje x nekonecnekrat dlouho - nejlépe vůbec nespouštět

V případě, že se funkci repeat předají dva parametry, je první z nich považován za délku vytvářené sekvence:

(repeat 10 42)
(42 42 42 42 42 42 42 42 42 42)
(repeat 100 "Nesmim v zime olizovat zabradli")

Konečně se dostáváme k zajímavému tématu – k funkci map. V nejjednodušším případě funkce map aplikuje nějakou jinou funkci na všechny prvky nějaké sekvence a výsledkem této operace je nová (lazy) sekvence. Sice to může znít složitě, ale použití funkce map je ve skutečnosti dosti jednoduché, protože již víme, že funkce jsou v Clojure plnohodnotným datovým typem a tudíž je lze předat jako parametr jiné funkci.

Aplikace funkce inc na každý prvek sekvence (zde vektoru):

(map inc [1 2 3 4 5 6 7 8])
(2 3 4 5 6 7 8 9)

U seznamů si musíme dát pozor na jejich quotování:

(map inc '(1 2 3 4 5 6 7 8))
(2 3 4 5 6 7 8 9)

Sekvenci lze samozřejmě vytvořit i pomocí funkcí range a repeat:

(map inc (range 1 9))
(2 3 4 5 6 7 8 9)

Ještě zajímavější je aplikace nějaké funkce s větší aritou než 1 na dvojici, trojici atd. sekvencí:

(map * [1 2 3 4] [5 6 7 8])
(5 12 21 32)

Dtto se seznamy:

(map * '(1 2 3 4) '(5 6 7 8))
(5 12 21 32)
(map * (range 1 5) (range 5 9))
(5 12 21 32)

Hmm, tato číselná řada se tuším nějak jmenovala…

(map / (range 10) (range 1 10))
(0 1/2 2/3 3/4 4/5 5/6 6/7 7/8 8/9)

Samozřejmě lze použít například i funkci „větší než“:

bitcoin školení listopad 24

(map > (range 1 10) (range 10 1 -1))
(false false false false false true true true true)

Popř. compare vracející –1, 0, či 1:

(map compare (range 1 10) (range 10 1 -1))
(-1 -1 -1 -1 -1 1 1 1 1)

To však není zdaleka vše!, ovšem některé možnosti použití funkce map a podobných funkcí si necháme na další díl tohoto seriálu.

9. Odkazy na Internetu

  1. Clojure home page
    http://clojure.org/downloads
  2. Clojure – Functional Programming for the JVM
    http://java.ociweb.com/mar­k/clojure/article.html
  3. Clojure quick reference
    http://faustus.webatu.com/clj-quick-ref.html
  4. 4Clojure
    http://www.4clojure.com/
  5. ClojureDoc
    http://clojuredocs.org/
  6. Clojure (Wikipedia EN)
    http://en.wikipedia.org/wiki/Clojure
  7. Clojure (Wikipedia CS)
    http://cs.wikipedia.org/wiki/Clojure
  8. Riastradh's Lisp Style Rules
    http://mumble.net/~campbe­ll/scheme/style.txt
  9. Dynamic Languages Strike Back
    http://steve-yegge.blogspot.cz/2008/05/dynamic-languages-strike-back.html
  10. Scripting: Higher Level Programming for the 21st Century
    http://www.tcl.tk/doc/scripting.html
  11. Java Virtual Machine Support for Non-Java Languages
    http://docs.oracle.com/ja­vase/7/docs/technotes/gui­des/vm/multiple-language-support.html
  12. New JDK 7 Feature: Support for Dynamically Typed Languages in the Java Virtual Machine
    http://java.sun.com/develo­per/technicalArticles/Dyn­TypeLang/
  13. JSR 223: Scripting for the JavaTM Platform
    http://jcp.org/en/jsr/detail?id=223
  14. JSR 292: Supporting Dynamically Typed Languages on the JavaTM Platform
    http://jcp.org/en/jsr/detail?id=292
  15. Java 7: A complete invokedynamic example
    http://niklasschlimm.blog­spot.com/2012/02/java-7-complete-invokedynamic-example.html
  16. InvokeDynamic: Actually Useful?
    http://blog.headius.com/2007/01/in­vokedynamic-actually-useful.html
  17. A First Taste of InvokeDynamic
    http://blog.headius.com/2008/09/first-taste-of-invokedynamic.html
  18. Java 6 try/finally compilation without jsr/ret
    http://cliffhacks.blogspot­.com/2008/02/java-6-tryfinally-compilation-without.html
  19. An empirical study of Java bytecode programs
    http://www.mendeley.com/research/an-empirical-study-of-java-bytecode-programs/
  20. Java quick guide: JVM Instruction Set (tabulka všech instrukcí JVM)
    http://www.mobilefish.com/tu­torials/java/java_quickgu­ide_jvm_instruction_set.html
  21. The JVM Instruction Set
    http://mpdeboer.home.xs4a­ll.nl/scriptie/node14.html
  22. Control Flow in the Java Virtual Machine
    http://www.artima.com/under­thehood/flowP.html
  23. Root.cz: Využití komprimovaných ukazatelů na objekty v JVM
    http://www.root.cz/clanky/vyuziti-komprimovanych-ukazatelu-na-objekty-v-nbsp-jvm/
  24. Root.cz: JamVM aneb alternativa k HotSpotu nejenom pro embedded zařízení a chytré telefony
    http://www.root.cz/clanky/jamvm-aneb-alternativa-k-hotspotu-nejenom-pro-embedded-zarizeni-tablety-a-chytre-telefony/
  25. The JavaTM Virtual Machine Specification, Second Edition
    http://java.sun.com/docs/bo­oks/jvms/second_edition/html/VMSpec­TOC.doc.html
  26. The class File Format
    http://java.sun.com/docs/bo­oks/jvms/second_edition/html/Clas­sFile.doc.html
  27. javap – The Java Class File Disassembler
    http://docs.oracle.com/ja­vase/1.4.2/docs/tooldocs/win­dows/javap.html
  28. javap-java-1.6.0-openjdk(1) – Linux man page
    http://linux.die.net/man/1/javap-java-1.6.0-openjdk
  29. Using javap
    http://www.idevelopment.in­fo/data/Programming/java/mis­cellaneous_java/Using_javap­.html
  30. Examine class files with the javap command
    http://www.techrepublic.com/ar­ticle/examine-class-files-with-the-javap-command/5815354
  31. BCEL Home page
    http://commons.apache.org/bcel/
  32. BCEL Manual
    http://commons.apache.org/bcel/ma­nual.html
  33. Byte Code Engineering Library (Wikipedia)
    http://en.wikipedia.org/wiki/BCEL
  34. Java programming dynamics, Part 7: Bytecode engineering with BCEL
    http://www.ibm.com/develo­perworks/java/library/j-dyn0414/
  35. Bytecode Engineering
    http://book.chinaunix.net/spe­cial/ebook/Core_Java2_Volu­me2AF/0131118269/ch13lev1sec6­.html
  36. BCEL Tutorial
    http://www.smfsupport.com/sup­port/java/bcel-tutorial!/
  37. ASM Home page
    http://asm.ow2.org/
  38. Seznam nástrojů využívajících projekt ASM
    http://asm.ow2.org/users.html
  39. ObjectWeb ASM (Wikipedia)
    http://en.wikipedia.org/wi­ki/ObjectWeb_ASM
  40. Java Bytecode BCEL vs ASM
    http://james.onegoodcooki­e.com/2005/10/26/java-bytecode-bcel-vs-asm/
  41. Bytecode Outline plugin for Eclipse (screenshoty + info)
    http://asm.ow2.org/eclipse/index.html
  42. aspectj (Eclipse)
    http://www.eclipse.org/aspectj/
  43. Aspect-oriented programming (Wikipedia)
    http://en.wikipedia.org/wi­ki/Aspect_oriented_program­ming
  44. AspectJ (Wikipedia)
    http://en.wikipedia.org/wiki/AspectJ
  45. EMMA: a free Java code coverage tool
    http://emma.sourceforge.net/
  46. Cobertura
    http://cobertura.sourceforge.net/
  47. FindBugs
    http://findbugs.sourceforge.net/
  48. GNU Classpath
    www.gnu.org/s/classpath/
  49. Java VMs Compared
    http://bugblogger.com/java-vms-compared-160/
  50. JSRs: Java Specification Requests – JSR 223: Scripting for the Java Platform
    http://www.jcp.org/en/jsr/de­tail?id=223
  51. Scripting for the Java Platform
    http://java.sun.com/develo­per/technicalArticles/J2SE/Des­ktop/scripting/
  52. Scripting for the Java Platform (Wikipedia)
    http://en.wikipedia.org/wi­ki/Scripting_for_the_Java_Plat­form
  53. Java Community Process
    http://en.wikipedia.org/wi­ki/Java_Specification_Requ­est
  54. Java HotSpot VM Options
    http://www.oracle.com/technet­work/java/javase/tech/vmop­tions-jsp-140102.html
  55. Great Computer Language Shootout
    http://c2.com/cgi/wiki?Gre­atComputerLanguageShootout
  56. Java performance
    http://en.wikipedia.org/wi­ki/Java_performance
  57. Trying the prototype
    http://mail.openjdk.java.net/pi­permail/lambda-dev/2010-August/002179.html
  58. Better closures (for Java)
    http://blogs.sun.com/jrose/en­try/better_closures
  59. Lambdas in Java: An In-Depth Analysis
    http://www.infoq.com/articles/lambdas-java-analysis
  60. Class ReflectiveOperationException
    http://download.java.net/jdk7/doc­s/api/java/lang/Reflective­OperationException.html
  61. Scala Programming Language
    http://www.scala-lang.org/
  62. Run Scala in Apache Tomcat in 10 minutes
    http://www.softwaresecret­weapons.com/jspwiki/run-scala-in-apache-tomcat-in-10-minutes
  63. Fast Web Development With Scala
    http://chasethedevil.blog­spot.cz/2007/09/fast-web-development-with-scala.html
  64. Top five scripting languages on the JVM
    http://www.infoworld.com/d/developer-world/top-five-scripting-languages-the-jvm-855
  65. Proposal: Indexing access syntax for Lists and Maps
    http://mail.openjdk.java.net/pi­permail/coin-dev/2009-March/001108.html
  66. Proposal: Elvis and Other Null-Safe Operators
    http://mail.openjdk.java.net/pi­permail/coin-dev/2009-March/000047.html
  67. Java 7 : Oracle pushes a first version of closures
    http://www.baptiste-wicht.com/2010/05/oracle-pushes-a-first-version-of-closures/
  68. Groovy: An agile dynamic language for the Java Platform
    http://groovy.codehaus.org/Operators
  69. Better Strategies for Null Handling in Java
    http://www.slideshare.net/Step­han.Schmidt/better-strategies-for-null-handling-in-java
  70. Control Flow in the Java Virtual Machine
    http://www.artima.com/under­thehood/flowP.html
  71. Java Virtual Machine
    http://en.wikipedia.org/wi­ki/Java_virtual_machine
  72. ==, .equals(), compareTo(), and compare()
    http://leepoint.net/notes-java/data/expressions/22com­pareobjects.html
  73. New JDK7 features
    http://openjdk.java.net/pro­jects/jdk7/features/
  74. Project Coin: Bringing it to a Close(able)
    http://blogs.sun.com/darcy/en­try/project_coin_bring_clo­se
  75. CloseableFinder source code
    http://blogs.sun.com/darcy/re­source/ProjectCoin/Closea­bleFinder.java
  76. Joe Darcy blog about JDK
    http://blogs.sun.com/darcy
  77. Java 7 – more dynamics
    http://www.baptiste-wicht.com/2010/04/java-7-more-dynamics/
  78. New JDK 7 Feature: Support for Dynamically Typed Languages in the Java Virtual Machine
    http://java.sun.com/develo­per/technicalArticles/Dyn­TypeLang/index.html

Autor článku

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