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
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
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 rest i next vrací zbylé prvky v sekvenci, ovšem liší se tím, jaká hodnota se vrátí ve chvíli, kdy již v sekvenci nezbyly žádné prvky (kromě prvního). V tomto případě vrátí rest prázdnou sekvenci (například prázdný seznam), zatímco funkce next vrátí 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ž“:
(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
- Clojure home page
http://clojure.org/downloads - Clojure – Functional Programming for the JVM
http://java.ociweb.com/mark/clojure/article.html - Clojure quick reference
http://faustus.webatu.com/clj-quick-ref.html - 4Clojure
http://www.4clojure.com/ - ClojureDoc
http://clojuredocs.org/ - Clojure (Wikipedia EN)
http://en.wikipedia.org/wiki/Clojure - Clojure (Wikipedia CS)
http://cs.wikipedia.org/wiki/Clojure - Riastradh's Lisp Style Rules
http://mumble.net/~campbell/scheme/style.txt - Dynamic Languages Strike Back
http://steve-yegge.blogspot.cz/2008/05/dynamic-languages-strike-back.html - Scripting: Higher Level Programming for the 21st Century
http://www.tcl.tk/doc/scripting.html - Java Virtual Machine Support for Non-Java Languages
http://docs.oracle.com/javase/7/docs/technotes/guides/vm/multiple-language-support.html - New JDK 7 Feature: Support for Dynamically Typed Languages in the Java Virtual Machine
http://java.sun.com/developer/technicalArticles/DynTypeLang/ - JSR 223: Scripting for the JavaTM Platform
http://jcp.org/en/jsr/detail?id=223 - JSR 292: Supporting Dynamically Typed Languages on the JavaTM Platform
http://jcp.org/en/jsr/detail?id=292 - Java 7: A complete invokedynamic example
http://niklasschlimm.blogspot.com/2012/02/java-7-complete-invokedynamic-example.html - InvokeDynamic: Actually Useful?
http://blog.headius.com/2007/01/invokedynamic-actually-useful.html - A First Taste of InvokeDynamic
http://blog.headius.com/2008/09/first-taste-of-invokedynamic.html - Java 6 try/finally compilation without jsr/ret
http://cliffhacks.blogspot.com/2008/02/java-6-tryfinally-compilation-without.html - An empirical study of Java bytecode programs
http://www.mendeley.com/research/an-empirical-study-of-java-bytecode-programs/ - Java quick guide: JVM Instruction Set (tabulka všech instrukcí JVM)
http://www.mobilefish.com/tutorials/java/java_quickguide_jvm_instruction_set.html - The JVM Instruction Set
http://mpdeboer.home.xs4all.nl/scriptie/node14.html - Control Flow in the Java Virtual Machine
http://www.artima.com/underthehood/flowP.html - Root.cz: Využití komprimovaných ukazatelů na objekty v JVM
http://www.root.cz/clanky/vyuziti-komprimovanych-ukazatelu-na-objekty-v-nbsp-jvm/ - 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/ - The JavaTM Virtual Machine Specification, Second Edition
http://java.sun.com/docs/books/jvms/second_edition/html/VMSpecTOC.doc.html - The class File Format
http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html - javap – The Java Class File Disassembler
http://docs.oracle.com/javase/1.4.2/docs/tooldocs/windows/javap.html - javap-java-1.6.0-openjdk(1) – Linux man page
http://linux.die.net/man/1/javap-java-1.6.0-openjdk - Using javap
http://www.idevelopment.info/data/Programming/java/miscellaneous_java/Using_javap.html - Examine class files with the javap command
http://www.techrepublic.com/article/examine-class-files-with-the-javap-command/5815354 - BCEL Home page
http://commons.apache.org/bcel/ - BCEL Manual
http://commons.apache.org/bcel/manual.html - Byte Code Engineering Library (Wikipedia)
http://en.wikipedia.org/wiki/BCEL - Java programming dynamics, Part 7: Bytecode engineering with BCEL
http://www.ibm.com/developerworks/java/library/j-dyn0414/ - Bytecode Engineering
http://book.chinaunix.net/special/ebook/Core_Java2_Volume2AF/0131118269/ch13lev1sec6.html - BCEL Tutorial
http://www.smfsupport.com/support/java/bcel-tutorial!/ - ASM Home page
http://asm.ow2.org/ - Seznam nástrojů využívajících projekt ASM
http://asm.ow2.org/users.html - ObjectWeb ASM (Wikipedia)
http://en.wikipedia.org/wiki/ObjectWeb_ASM - Java Bytecode BCEL vs ASM
http://james.onegoodcookie.com/2005/10/26/java-bytecode-bcel-vs-asm/ - Bytecode Outline plugin for Eclipse (screenshoty + info)
http://asm.ow2.org/eclipse/index.html - aspectj (Eclipse)
http://www.eclipse.org/aspectj/ - Aspect-oriented programming (Wikipedia)
http://en.wikipedia.org/wiki/Aspect_oriented_programming - AspectJ (Wikipedia)
http://en.wikipedia.org/wiki/AspectJ - EMMA: a free Java code coverage tool
http://emma.sourceforge.net/ - Cobertura
http://cobertura.sourceforge.net/ - FindBugs
http://findbugs.sourceforge.net/ - GNU Classpath
www.gnu.org/s/classpath/ - Java VMs Compared
http://bugblogger.com/java-vms-compared-160/ - JSRs: Java Specification Requests – JSR 223: Scripting for the Java Platform
http://www.jcp.org/en/jsr/detail?id=223 - Scripting for the Java Platform
http://java.sun.com/developer/technicalArticles/J2SE/Desktop/scripting/ - Scripting for the Java Platform (Wikipedia)
http://en.wikipedia.org/wiki/Scripting_for_the_Java_Platform - Java Community Process
http://en.wikipedia.org/wiki/Java_Specification_Request - Java HotSpot VM Options
http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html - Great Computer Language Shootout
http://c2.com/cgi/wiki?GreatComputerLanguageShootout - Java performance
http://en.wikipedia.org/wiki/Java_performance - Trying the prototype
http://mail.openjdk.java.net/pipermail/lambda-dev/2010-August/002179.html - Better closures (for Java)
http://blogs.sun.com/jrose/entry/better_closures - Lambdas in Java: An In-Depth Analysis
http://www.infoq.com/articles/lambdas-java-analysis - Class ReflectiveOperationException
http://download.java.net/jdk7/docs/api/java/lang/ReflectiveOperationException.html - Scala Programming Language
http://www.scala-lang.org/ - Run Scala in Apache Tomcat in 10 minutes
http://www.softwaresecretweapons.com/jspwiki/run-scala-in-apache-tomcat-in-10-minutes - Fast Web Development With Scala
http://chasethedevil.blogspot.cz/2007/09/fast-web-development-with-scala.html - Top five scripting languages on the JVM
http://www.infoworld.com/d/developer-world/top-five-scripting-languages-the-jvm-855 - Proposal: Indexing access syntax for Lists and Maps
http://mail.openjdk.java.net/pipermail/coin-dev/2009-March/001108.html - Proposal: Elvis and Other Null-Safe Operators
http://mail.openjdk.java.net/pipermail/coin-dev/2009-March/000047.html - Java 7 : Oracle pushes a first version of closures
http://www.baptiste-wicht.com/2010/05/oracle-pushes-a-first-version-of-closures/ - Groovy: An agile dynamic language for the Java Platform
http://groovy.codehaus.org/Operators - Better Strategies for Null Handling in Java
http://www.slideshare.net/Stephan.Schmidt/better-strategies-for-null-handling-in-java - Control Flow in the Java Virtual Machine
http://www.artima.com/underthehood/flowP.html - Java Virtual Machine
http://en.wikipedia.org/wiki/Java_virtual_machine - ==, .equals(), compareTo(), and compare()
http://leepoint.net/notes-java/data/expressions/22compareobjects.html - New JDK7 features
http://openjdk.java.net/projects/jdk7/features/ - Project Coin: Bringing it to a Close(able)
http://blogs.sun.com/darcy/entry/project_coin_bring_close - CloseableFinder source code
http://blogs.sun.com/darcy/resource/ProjectCoin/CloseableFinder.java - Joe Darcy blog about JDK
http://blogs.sun.com/darcy - Java 7 – more dynamics
http://www.baptiste-wicht.com/2010/04/java-7-more-dynamics/ - New JDK 7 Feature: Support for Dynamically Typed Languages in the Java Virtual Machine
http://java.sun.com/developer/technicalArticles/DynTypeLang/index.html