Hlavní navigace

Clojure a bezpečné aplikace pro JVM: sekvence, lazy sekvence a paralelní programy

10. 7. 2012
Doba čtení: 19 minut

Sdílet

V dnešní části seriálu o programovacím jazyce Java i o vlastnostech JVM se již popáté budeme zabývat jazykem Clojure postaveným nad virtuálním strojem Javy. Řekneme si další informace o sekvencích i lazy sekvencích, včetně způsobu jejich základního využití při paralelních výpočtech.

Obsah

1. Krátké zopakování předchozí části seriálu: sekvence a lazy sekvence

2. Funkce nth a take

3. Funkce take-while

4. Přímé vytvoření lazy sekvence

5. Použití iterate pro vytvoření lazy sekvence

6. Funkce filter a apply

7. Sekvence a paralelní výpočty

8. Funkce pmap

9. Odkazy na Internetu

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 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. 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:

CS24_early

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

  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

Byl pro vás článek přínosný?

Autor článku

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