Obsah
1. Krátké zopakování předchozí části seriálu: sekvence a lazy sekvence
4. Přímé vytvoření lazy sekvence
5. Použití iterate pro vytvoření lazy sekvence
7. Sekvence a paralelní výpočty
1. Krátké zopakování předchozí části seriálu: sekvence a lazy sekvence
V předchozí části seriálu o programovacím jazyce Java i o vlastnostech virtuálního stroje tohoto jazyka jsme se zabývali popisem kolekcí (tj. seznamů, vektorů, map a množin) a taktéž popisem sekvencí i takzvaných lazy sekvencí. Sekvence, které svými základními možnostmi zhruba odpovídají iterátorům známým z programovacího jazyka Java, jsou velmi důležitou součástí Clojure a najdeme je v prakticky všech aplikacích, které jsou v Clojure naprogramovány. V některých typech aplikací může být taktéž užitečná možnost zpracovat sekvenci (v tomto případě nejčastěji lazy sekvenci) s využitím funkce pmap, neboli parallel map. Zajímavé je, že i výsledkem funkce pmap je lazy sekvence, tj. prvky této sekvence mohou být vyhodnoceny až ve chvíli, kdy jsou skutečně zapotřebí.
Minule jsme si taktéž řekli, že 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. V případě lazy sekvencí se prvky vrácené pomocí funkce first vyhodnocují až za běhu, typicky s využitím nějaké generátorové funkce (například range). 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.
Z některých funkcí pracujících se sekvencemi a lazy sekvencemi jsme si popsali především funkci range (generování potenciálně nekonečné aritmetické řady), repeat (generování potenciálně nekonečné řady stejných hodnot) a nakonec jsme se zabývali velmi užitečnou funkcí map, jejímž výsledkem je lazy sekvence a to opět sekvence, která může mít v některých případech nekonečný počet prvků. Bez dalšího podrobnějšího popisu si ještě ukážeme několik příkladů uvedených již v předchozí části tohoto seriálu (tam taktéž najdete popis těchto příkladů):
Sekvence od nuly 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)
Použití záporného kroku různého od jedničky:
user=> (range 20 10 -2) (20 18 16 14 12)
Vytvoření sekvence obsahující deset hodnot 42:
(repeat 10 42) (42 42 42 42 42 42 42 42 42 42)
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)
Sekvenci lze samozřejmě vytvořit i s využitím funkcí range a repeat (přičemž první zmiňovaná funkce je mnohem užitečnější):
(map inc (range 1 9)) (2 3 4 5 6 7 8 9)
2. Funkce nth a take
Při práci s lazy sekvencemi, které obsahují nekonečný počet prvků, si musíme dát pozor na to, aby se náhodou nespustilo vyhodnocení celé (nekonečné) sekvence. V reálných programech k tomuto problému v naprosté většině případů nedochází, už jen z toho důvodu, že například funkce map jako svůj parametr akceptuje lazy sekvenci a jejím výsledkem je taktéž lazy sekvence. To například znamená, že následující dvojice příkazů se provede prakticky ihned, aniž by se interpret programovacího jazyka Clojure snažil o vyhodnocení všech prvků vytvářených funkcí range:
user=> (def lazy-seq-1 (range)) #'user/lazy-seq-1 user=> (def lazy-seq-2 (map inc lazy-seq-1)) #'user/lazy-seq-2
Problém však může nastat v případě, kdy se o vyhodnocení všech prvků nekonečné sekvence bude snažit samotná smyčka REPL, takže například jeden z následujících dvou příkazů skončí výpisem VELMI dlouhé řady číselných hodnot:
user=> (range) user=> (map inc lazy-seq-1)
My ovšem v následujících kapitolách budeme muset zjistit a vypsat hodnotu alespoň několika prvků nekonečných lazy sekvencí. K tomuto účelu nám dobře poslouží funkce nth, take a někdy taktéž poněkud složitější funkce take-while. Nejjednodušší z této trojice funkcí je funkce nth, jež – jak jste již asi z jejího názvu uhodli – vrací n-tý prvek sekvence, což většinou znamená, že se vyhodnotí i předchozích n-1 prvků (ovšem ve skutečnosti se výsledky ukládají do vyrovnávací paměti, takže někdy k vyhodnocení nedochází):
user=> (nth (range) 10) 10
user=> (nth (range 1 100 2) 10) 21
Následuje poněkud umělý příklad, protože funkce repeat vrací nekonečnou lazy sekvenci stejných hodnot, tudíž je ve skutečnosti jedno, který prvek sekvence získáme:
user=> (nth (repeat "opakujeme") 10) "opakujeme"
Vraťme se nyní k úvodnímu příkladu této kapitoly:
user=> (def lazy-seq-1 (range)) #'user/lazy-seq-1 user=> (def lazy-seq-2 (map inc lazy-seq-1)) #'user/lazy-seq-2 user=> (nth lazy-seq-1 10) 10 user=> (nth lazy-seq-2 10) 11
Zatímco funkce nth vrátí konkrétní prvek z lazy sekvence, je další užitečná funkce – take – poněkud obecnější, neboť ta vrací prvních n prvků lazy sekvence. Ovšem výsledkem není vektor či seznam těchto prvků, ale taktéž lazy sekvence, což znamená, že k vyhodnocení (získání) prvků dochází později a někdy taktéž vůbec ne. My ovšem v našich příkladech budeme výsledek funkce take vypisovat pomocí REPL, takže k vyhodnocení dojde vždy:
user=> (take 10 (range)) (0 1 2 3 4 5 6 7 8 9)
user=> (take 10 (repeat :x)) (:x :x :x :x :x :x :x :x :x :x)
user=> (take 10 (range 0 10000 3)) (0 3 6 9 12 15 18 21 24 27)
Opět se vraťme k prvnímu příkladu z této kapitoly:
user=> (def lazy-seq-1 (range)) #'user/lazy-seq-1 user=> (def lazy-seq-2 (map inc lazy-seq-1)) #'user/lazy-seq-2 user=> (take 10 lazy-seq-1) (0 1 2 3 4 5 6 7 8 9) user=> (take 10 lazy-seq-2) (1 2 3 4 5 6 7 8 9 10)
Povšimněte si toho, že parametry funkce nth a funkce take jsou uvedeny v opačném pořadí, což je poněkud matoucí.
3. Funkce take-while
V některých případech nám bude užitečná i poněkud komplikovanější funkce, která je nazvaná take-while. Zatímco u funkce take se přímo zadával počet prvků lazy sekvence, která se má vrátit, je v případě funkce take-while namísto konstantního počtu prvků výsledné sekvence předán predikát, tj. funkce s (v tomto případě) jedním parametrem, jejímž výsledkem by měla být pravdivostní hodnota true nebo false (asi si již nemusíme připomínat, že funkce lze v Clojure použít na stejném místě, jako jiné typy hodnot, tedy i funkce lze bez problémů předat jako parametr do jiné funkce).
Návratovou hodnotou funkce take-while je opět lazy sekvence získaná ze vstupní sekvence, ovšem vráceno je pouze prvních n prvků, pro něž predikát vrací hodnotu true. Nejedná se však o klasický filtr (viz též další kapitoly), protože ihned ve chvíli, kdy predikát poprvé vrátí hodnotu false, je lazy sekvence ukončena. Pokud vrátí predikát hodnotu false již při prvním volání, je výsledkem prázdná sekvence, pokud naopak vrací hodnotu true vždy, vrátí se potenciálně nekonečná lazy sekvence (což však někdy nemusí vadit, pokud se tedy nebudeme snažit o výpis všech prvků). Vzhledem k určitým omezením take-while je nutné, aby měl predikát pouze jeden parametr, což většinou znamená, že si musíme vypomoci novou funkcí (popř. anonymní funkcí).
Následuje příklad velmi jednoduchého pomocného predikátu, tj. funkce, která vrací hodnotu true v případě, že je jí předaný parametr menší nebo roven číselné konstantě 42:
user=> (defn end-of-lazy-seq [x] (<= x 42)) #'user/end-of-lazy-seq
Tento predikát již můžeme použít ve funkci take-while aplikované na nekonečnou aritmetickou řadu generovanou funkcí range:
user=> (take-while end-of-lazy-seq (range)) (0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42)
Popř:
user=> (take-while end-of-lazy-seq (range 1 100 3)) (1 4 7 10 13 16 19 22 25 28 31 34 37 40)
Mnozí programátoři dávají přednost použití anonymních funkcí, což je mnohdy velmi elegantní řešení:
user=> (take-while (fn [x] (<= x 42)) (range)) (0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42)
Jen v některých případech lze přímo použít predikáty, které jsou součástí základních knihoven programovacího jazyka Clojure:
; příklad konečné lazy sekvence se záporným krokem user=> (range 10 -10 -1) (10 9 8 7 6 5 4 3 2 1 0 -1 -2 -3 -4 -5 -6 -7 -8 -9) ; získáme a následně vypíšeme pouze kladné hodnoty z této sekvence user=> (take-while pos? (range 10 -10 -1)) (10 9 8 7 6 5 4 3 2 1) ; ovšem pokud prekikát vrátí hned v prvním kroku hodnotu false, ; je výsledkem prázdná sekvence user=> (take-while neg? (range 10 -10 -1)) ()
4. Přímé vytvoření lazy sekvence
V demonstračních příkladech jsme se již setkali minimálně se třemi funkcemi, jejichž výsledkem je lazy sekvence. Jednalo se o funkce range, repeat a map. Ovšem prozatím vlastně ani nevíme, jakým způsobem je možné vytvořit vlastní lazy sekvenci. Ve skutečnosti to ani není příliš složité, protože nám programovací jazyk Clojure pro tyto účely poskytuje makro nazvané přímočaře lazy-seq. S využitím tohoto makra lze ve velkém množství případů generovat lazy sekvenci takovým způsobem, aniž by bylo nutné při výpočtu použít rekurzi, jelikož již z předchozích částí tohoto seriálu víme, že rekurze s sebou přináší i určité problémy při ukládání parametrů a návratových adres na zásobník a řešení ve formě tail rekurze není vždy snadno dostupné. Podívejme se nyní na způsob využití makra lazy-seq v praxi.
Se znalostí funkcí nth, take a take-while si nyní můžeme naprogramovat vlastní čítač, který však namísto aritmetické řady (což je záležitost standardní funkce range, kterou již známe) bude vytvářet řadu geometrickou, kterou lze v rámci zjednodušení považovat za nekonečnou řadu. Problémy nastanou s přetečením výsledků i to však lze částečně řešit s využitím BigDecimal. Takový čítač by mohl být deklarován jako funkce vracející lazy sekvenci, kterému se předá hodnota prvku a0 a koeficient q:
(defn geom-counter [a_n q] (lazy-seq (cons a_n (geom-counter (* a_n q) q))))
Funkci cons již známe – jde o konstruktor seznamu (resp. obecné sekvence), který vytvoří nový seznam připojením dalšího prvku. Povšimněte si, že při deklaraci funkce geom-counter je sice použita rekurze, takže by se mohlo zdát, že jsme se problému s přetečením zásobníku nakonec nevyhnuli, ovšem díky použití makra lazy-seq je výpočet dalšího prvku lazy sekvence spuštěn až ve chvíli, kdy je známý předchozí prvek, tudíž k rekurzi ve skutečnosti nedojde. Můžeme si to ostatně vyzkoušet.
Prvních deset prvků mocnin dvojky:
user=> (take 10 (geom-counter 1 2)) (1 2 4 8 16 32 64 128 256 512)
Vrácení desátého prvku stejné geometrické řady:
user=> (nth (geom-counter 1 2) 10) 1024
Můžeme se pokusit získat tisící prvek, ovšem aby se zabránilo přetečení celých čísel, je nutné interpretru Clojure „vnutit“ konstantu typu BigDecimal (suffix M u hodnoty prvku a0):
user=> (nth (geom-counter 1M 2) 1000) 10715086071862673209484250490600018105614048117055336074437503883703510 51124936122493198378815695858127594672917553146825187145285692314043598 45775746985748039345677748242309854210746050623711418779541821530464749 83581941267398767559165543946077062914571196477686542167660429831652624 386837205668069376M
Co by se však stalo, kdyby se namísto generátoru lazy sekvence použil pouhý přímočarý rekurzivní výpočet? Můžeme si to snadno otestovat:
(defn geom-counter [a_n q] (cons a_n (geom-counter (* a_n q) q)))
Při vyhodnocení n-tého prvku sekvence s využitím funkce nth samozřejmě dojde k nekonečné rekurzi a po malé chvíli i k přetečení zásobníku. Nikde totiž nemáme zadanou podmínku pro ukončení rekurze, což však v případě předchozího příkladu s generátorem lazy sekvence vůbec nevadilo:
user=> (nth (geom-counter 1 2) 1) ArithmeticException integer overflow clojure.lang.Numbers.throwIntOverflow (Numbers.java:1388)
5. Použití iterate pro vytvoření lazy sekvence
Využití makra lazy-seq pro vytvoření nové lazy sekvence je sice v mnoha případech užitečné, ovšem někdy je jednodušší a přímočařejší namísto tohoto makra použít funkci iterate, která se taktéž nachází ve standardní knihovně programovacího jazyka Clojure. Funkce iterate generuje nekonečnou sekvenci takovým způsobem, že postupně aplikuje vybranou funkci (svůj první parametr) na prvek získaný v předchozí iteraci. Počáteční hodnota prvku pro nultou iteraci je předána funkci iterate jako druhý parametr. Dobrým příkladem použití funkce iterate je lazy sekvence obsahující všechna kladná čísla. Ta se získají jednoduše – prvním prvkem celé sekvence je hodnota nula (popř. hodnota jedna, pokud potřebujeme, aby řada nezačínala od nuly) a každý další prvek se získá z prvku předchozího s využitím nám již známé funkce inc (increment). Pouze si opět musíme dát pozor na to, aby se smyčka REPL nesnažila o vyhodnocení a o následný výpis všech kladných čísel:
user=> (def kladna-cisla (iterate inc 0)) #'user/kladna-cisla user=> (nth kladna-cisla 42) 42 user=> (take 10 kladna-cisla) (0 1 2 3 4 5 6 7 8 9)
Popř. si můžeme vygenerovat geometrickou řadu:
user=> (defn two-times [x] (* x 2)) #'user/two-times user=> (def power-2 (iterate two-times 1)) #'user/power-2 user=> (take 10 power-2) (1 2 4 8 16 32 64 128 256 512)
Pokud preferujete používání anonymních funkcí, lze počet forem zmenšit na pouhé dvě formy:
user=> (def power-2 (iterate (fn [x] (* x 2)) 1)) #'user/power-2 user=> (take 10 power-2) (1 2 4 8 16 32 64 128 256 512)
Se znalostí funkce iterate je možné vytvořit lazy sekvenci využívající čítač například takto:
user=> (defn lazy-counter [a_n q] (iterate (fn [n] (* n q)) a_n)) #'user/lazy-counter user=> (take 10 (lazy-counter 2 3)) (2 6 18 54 162 486 1458 4374 13122 39366)
První one-liner vypadá poněkud nečitelně, takže ho přepišme tak, aby byly vidět oba parametry funkce iterate – prvním parametrem je anonymní funkce pro výpočet n+1 prvku na základě n-tého prvku, druhým parametrem je hodnota prvního prvku v řadě:
(defn lazy-counter [a_n q] (iterate (fn [n] (* n q)) a_n))
6. Funkce filter a apply
Při vytváření reálných aplikací s využitím programovacího jazyka Clojure se taktéž často můžeme setkat s funkcemi nazvanými filter a apply. Jedná se o známé funkce, které lze najít v mnoha funkcionálních programovacích jazycích. Funkce filter je v určitém pohledu podobná již dříve popsané funkci take-while, protože i zde je na lazy sekvenci aplikován nějaký predikát, tj. funkce zavolaná pro každý prvek zpracovávané lazy sekvence, která vrací pravdivostní hodnotu true nebo false. Výsledkem práce funkce filter je další lazy sekvence, která obsahuje ty prvky vstupní sekvence, pro něž predikát vrátí pravdivostní hodnotu true. Vzhledem k tomu, že jak vstupem, tak i výstupem funkce filter je lazy sekvence, je i samotná funkce filter „líná“, tj. vyhodnocuje jednotlivé prvky až ve chvíli, kdy je to skutečně zapotřebí. Lze ji tedy použít i pro nekonečné lazy sekvence:
user=> (filter odd? (range 0 10)) (1 3 5 7 9) user=> (filter even? (range 0 10)) (0 2 4 6 8) (user=> (defn less-than-10 [x] (< x 10)) #'user/less-than-10 user=> (filter less-than-10 (range 0 1000)) (0 1 2 3 4 5 6 7 8 9)
Použití anonymní funkce:
user=> (filter (fn [x] (< x 10)) (range 0 1000)) (0 1 2 3 4 5 6 7 8 9)
Funkci apply lze předat dva a více parametrů. Prvním parametrem je vždy nějaká funkce a posledním parametrem je sekvence, popř. kolekce (která je ovšem též sekvencí). V případě, že je funkci apply předáno více parametrů, jsou tyto považovány za parametry předávané funkce. Návratovou hodnotou funkce apply je návratová hodnota funkce, které se předají všechny parametry, tudíž i prvky sekvence. Jinými slovy to znamená, že:
(apply fce a b c [d e f])
je totožné s voláním:
(fce a b c d e f)
Zajímavější však je, že funkce apply dokáže volání vybrané funkce s předáním parametrů vytvořit dynamicky, například takto:
(apply * (range 1 6))
Což odpovídá:
(* 1 2 3 4 5 6)
Tudíž se jedná o výpočet faktoriálu zapsaný nerekurzivně.
7. Sekvence a paralelní výpočty
V perexu článku bylo napsáno, že se dnes budeme zabývat i využitím sekvencí (a tudíž i kolekcí) v souvislosti s paralelními výpočty. Hned na úvod je nutné říci, že programovací jazyk Clojure v současné verzi nedokáže (a ani to evidentně není jeho cílem) automaticky převádět volání funkcí pracujících se sekvencemi takovým způsobem, aby se využil běh výpočtu ve více vláknech. Pokud například budeme pomocí funkce map aplikovat operaci inc na všechny prvky seznamu, bude tento výpočet prováděn v jednom vlákně prvek po prvku (i když ve skutečnosti dojde k „línému“ vyhodnocení). Toto chování má dva důvody – první důvod je ten, že interpret je stále velmi jednoduchý a druhým důvodem (možná závažnějším) je fakt, že samotné vytvoření mnoha vláken a jejich následná synchronizace při zpracování výsledků může být v některých případech náročnější na výpočetní výkon, než samotný sekvenční výpočet. Že se nejedná o pouhou domněnku si ukážeme hned v následující kapitole.
Programovací jazyk Clojure ovšem paralelní programování podporuje, a to hned na několika úrovních. Kromě takzvaných agentů, jejichž popisem se budeme zabývat hned v následující části tohoto seriálu, Clojure programátorům nabízí i trojici funkcí či maker nazvaných pmap (funkce), pvalues (funkce zajišťující vyhodnocení několika forem paralelně) a pcalls (v současnosti makro, které dokáže zavolat několik funkcí paralelně). Dnes se budeme zabývat především funkcí pmap, protože způsob jejího použití je velmi jednoduchý a vlastně ho již známe.
8. Funkce pmap
Funkce pmap se chová stejným způsobem jako již minule popsaná funkce map. V nejjednodušším případě map/pmap 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.
(map inc [1 2 3 4 5 6 7 8]) (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)
pmap vyhodnocuje jednotlivé prvky výsledné lazy sekvence paralelně, což s sebou může přinášet jak výhody, tak i nevýhody. Nespornou výhodou je „rozprostření“ výpočtu do více vláken, ovšem platíme za to určitou cenou, protože jednotlivá vlákna se musí vytvořit atd. Z tohoto důvodu se doporučuje používat funkci pmap především v těch případech, kdy jsou prováděné výpočty skutečně složité a/nebo obsahují například nějaké I/O operace atd. Ostatně časový rozdíl mezi funkcí map a pmap lze snadno zjistit s využitím funkce time, tedy jakýchsi stopek:
user=> (time (dorun (map inc (range 1 1000)))) "Elapsed time: 8.483482 msecs" nil user=> (time (dorun (pmap inc (range 1 1000)))) "Elapsed time: 246.6987 msecs" nil
Důvod, proč je zde pmap výrazně pomalejší než map je zřejmý – aby se provedl tak primitivní výpočet, jako je zvýšení hodnoty celého čísla o jedničku v samostatném vlákně, tak režie nutná pro vytvoření tohoto vlákna převyšuje jakýkoli benefit získaný paralelním výpočtem.
Sami si můžete vyzkoušet, jak se situace změní v případě dlouhotrvajících výpočtů:
(defn fibonacci [n] (if (< n 2) n (+ (fibonacci (- n 2)) (fibonacci (- n 1))))) (time (dorun (map fibonacci (range 100 150)))) (time (dorun (pmap fibonacci (range 100 150))))
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