Programovací jazyk Clojure 11: generátorová notace seznamu (list comprehension)

21. 8. 2012
Doba čtení: 17 minut

Sdílet

Ilustrační obrázek
Autor: Depositphotos – stori
Ilustrační obrázek
Dnes si popíšeme makro nazvané „for“, které v jazyce Clojure představuje generátorovou notaci seznamu, což je poněkud nepřesně přeložený termín „list comprehension“. Díky existenci tohoto makra je možné mnohdy i velmi výrazným způsobem zkrátit a zjednodušit zápis některých algoritmů.

Obsah

1. Programovací jazyk Clojure 11: generátorová notace seznamu (list comprehension)

2. Jednoduché formy využívající makro for

3. Složitější formy využívající makro for

4. Nekonečné sekvence aneb návrat k línému vyhodnocování

5. Použití modifikátorů :while a :when v makru for

6. Práce s více generátory současně

7. Demonstrační příklad – výpis hodnot hracích karet

8. Praktický příklad – výpočet Eulerova čísla s využitím makra for

9. Odkazy na Internetu

1. Programovací jazyk Clojure 11: generátorová notace seznamu (list comprehension)

V dnešní části seriálu o Javě a o virtuálním stroji Javy (JVM) se již pojedenácté vrátíme k popisu programovacího jazyka Clojure, který původně vznikl právě pro virtuální stroj Javy a později byl reimplementován i pro platformu .NET (resp. CLR) a v současnosti dokonce existuje i jeho varianta naprogramovaná v JavaScriptu. Dnes se budeme zabývat zejména popisem velmi užitečného makra for, s jehož využitím je možné i v Clojure používat takzvanou „generátorovou notaci seznamu“, což je poněkud nepřesně přeložený anglický termín „list comprehension“. O způsobu překladu tohoto termínu se vedly a pravděpodobně dodnes vedou poměrně vášnivé diskuse; já se zde budu držet překladu použitého v knize Ponořme se do Python(u) 3 (původně Dive Into Python 3)), kterou si můžete stáhnout na stránkách http://knihy.nic.cz/, popř. si zde objednat i papírovou verzi knihy.

S využitím generátorové notace seznamu je možné v programovacích jazycích, které tento zápis podporují, zapsat deklaraci vytvoření nového seznamu s využitím seznamu jiného, a to pomocí aplikace nějaké zvolené funkce na všechny prvky zdrojového seznamu. V mnoha programovacích jazycích je nutné pro generátorovou notaci seznamů používat zvláštní příkaz či deklaraci, tj. vlastně novou syntaktickou kategorii. Z těch známějších jazyků podporujících list comprehension se jedná například o Python, Haskell, Scala či Erlang. Programovací jazyk Clojure se však od těchto jazyků v podpoře pro generátorovou notaci seznamů liší, a to dokonce hned v několika ohledech. V Clojure se totiž nemusela zavádět žádná nová syntaxe, ale postačovalo vhodně naprogramovat makro (jeho kód je však poměrně dlouhý). Navíc je možné aplikovat zvolenou funkci nejenom na (konečný) seznam, ale na libovolnou (a to i nekonečnou) sekvenci, čímž ve výsledku můžeme taktéž získat nekonečnou sekvenci vyhodnocovanou líně (lazy sequence). Třetím rozdílem Clojure oproti jiným jazykům s podporou generátorových notací seznamů je to, že v Clojure je možné pracovat s libovolným množstvím zdrojových seznamů (přesněji řečeno sekvencí), což je téma šesté a sedmé kapitoly.

2. Jednoduché formy využívající makro for

Makro for, pomocí něhož je v jazyce Clojure implementována generátorová notace seznamů, má poměrně široké možnosti použití a při jeho zápisu lze dokonce použít i některé modifikátory, s nimiž se seznámíme v navazujících kapitolách. My však začneme náš popis velmi jednoduchými demonstračními příklady, které teprve později budeme rozšiřovat.

V prvním demonstračním příkladu je použit zdrojový (nepojmenovaný) seznam obsahující čtyři prvky, z něhož je s využitím makra for vytvořen nový seznam, který taktéž obsahuje čtyři prvky. Při vytváření nového seznamu se postupně na prvky zdrojového seznamu aplikovala funkce inc, která hodnotu každého prvku zvýšila o jedničku. Zajímavý je především způsob zápisu makra for, kde je v hranatých závorkách (tedy ve vektoru) nejprve uveden název lokální proměnné používané při výpočtu a za jménem proměnné je pak uveden zdrojový seznam. Apostrof před zdrojovým seznamem zabrání jeho vyhodnocení; pokud by zde nebyl uveden, došlo by k pokusu o zavolání funkce s názvem 1 s trojicí parametrů 2, 3 4. Posledním parametrem makra for je výraz (forma), jehož výsledek je použit pro konstrukci výsledného seznamu:

user=> (for [x '(1 2 3 4)] (inc x))
(2 3 4 5)

Zdrojový seznam samozřejmě může být navázán na nějakou proměnnou, což může vést k poněkud čitelnějšímu zápisu:

; vytvoření zdrojového seznamu
user=> (def seznam '(1 2 3 4))
#'user/seznam
 
; použití zdrojového seznamu ve "for"
user=> (for [x seznam] (inc x))
(2 3 4 5)

Lokální proměnnou x, která je postupně navazována na jednotlivé prvky zdrojového seznamu, je samozřejmě možné použít i ve složitějších výrazech; důležitý je pro nás pouze výsledek daného výrazu (jen minimálně se zde používají funkce s vedlejším efektem, i když i to je možné). V následujícím příkladu vznikne čtyřprvkový seznam obsahující numerické hodnoty typu zlomek (ratio):

user=> (for [x '(1 2 3 4)] (/ 1 x))
(1 1/2 1/3 1/4)

Namísto zdrojového seznamu je však v jazyce Clojure možné použít i jiný typ sekvence, například vektor:

user=> (for [x [10 20 30 40]] (/ 1 x))
(1/10 1/20 1/30 1/40)

(Připomeňme si, že u vektorů se nemusí použít apostrof pro zákaz jejich vyhodnocení).

3. Složitější formy využívající makro for

V úvodní kapitole jsme si řekli, že v programovacím jazyce Clojure se namísto zdrojového seznamu může použít libovolná sekvence, což jsme si vlastně již předvedli v předchozí kapitole použitím vektoru namísto seznamu. Snad nejtypičtějším generátorem sekvence je v Clojure funkce range, kterou můžeme v makru for využít na místě, kam jsme původně explicitně zapisovali zdrojový seznam či vektor:

user=> (for [x (range 1 10)] (/ 1 x))
(1 1/2 1/3 1/4 1/5 1/6 1/7 1/8 1/9)

Samozřejmě můžeme použít jakoukoli formu, jejímž výsledkem je sekvence, například kombinaci funkcí take a range:

user=> (for [x (take 10 (range))] (Math/sin x))
(0.0 0.8414709848078965 0.9092974268256817 0.1411200080598672
-0.7568024953079282 -0.9589242746631385 -0.27941549819892586
0.6569865987187891 0.9893582466233818 0.4121184852417566)

Sekvence, která je výsledkem zavolání makra for, se může zpracovat naprosto stejným způsobem, jako jiné sekvence. V následujícím demonstračním příkladu se všechny prvky vytvořené sekvence postupně sečtou s využitím funkce apply:

user=> (apply + (for [x '(1 2 3 4 5)] (/ 1 x)))
137/60

Zde se naopak výsledná sekvence setřídí:

user=> (sort (for [x [1 10 2 30 3 42]] (/ 10 x)))
(5/21 1/3 1 10/3 5 10)

Makra for je samozřejmě možné libovolným způsobem zanořovat, i když čitelnost je v tomto případě již velmi malá:

user=> (for [x (for [x (range 1 10)] (inc x))] (* x 2))
(4 6 8 10 12 14 16 18 20)

V předchozím příkladu je nejprve na sekvenci vytvořenou funkcí (range 1 10), tj. na sekvenci:

(1 2 3 4 5 6 7 8 9)

postupně aplikována funkce inc, takže jako mezivýsledek vznikne sekvence:

(2 3 4 5 6 7 8 9 10)

Na tuto sekvenci, resp. přesněji řečeno na prvky této sekvence, je postupně aplikována funkce (* … 2), čímž již dostaneme kýžený výsledek:

(4 6 8 10 12 14 16 18 20)

4. Nekonečné sekvence aneb návrat k línému vyhodnocování

Zajímavé bude zjistit, kdy přesně vlastně k vytvoření výsledného seznamu dochází. K tomu můžeme použít funkci s vedlejším efektem – výpisem nějaké informace na standardní výstup. Tato funkce může vypadat následovně:

; funkce nejprve vypíše informaci na standardní výstup
; a posléze vrátí hodnotu svého parametru zvýšenou o jedničku
user=> (defn increment [x] (println "Inkrementace cisla " x) (inc x))
#'user/increment
 
; test funkce increment
user=> (increment 42)
Inkrementace cisla  42
43

Pokud tuto funkci použijeme v makru for, můžeme získat základní informace o chování tohoto makra:

user=> (for [x '(1 2 3 4)] (increment x))
(Inkrementace cisla  1
Inkrementace cisla  2
2 Inkrementace cisla  3
3 Inkrementace cisla  4
4 5)

Na terminál se vypsal jak výsledek makra for, tedy seznam (2 3 4 5), tak i zprávy vypisované funkcí increment jako její vedlejší efekt. Stále však nevíme, kdy přesně se vlastně funkce increment bude volat. Zkusme tedy zakázat vyhodnocení výsledku makra for tak, že jeho výsledek uložíme do proměnné. Již z popisu líných sekvencí víme, že dokud nebudeme přistupovat k jednotlivým prvkům této proměnné, nemělo by dojít k vyhodnocení:

user=> (def result (for [x '(1 2 3 4)] (increment x)))
#'user/result

Skutečně – získali jsme sice novou proměnnou nazvanou result, ale nic jiného se na terminál nevypsalo, tudíž ani prozatím nedošlo k zavolání funkce increment. Vyhodnocení vynutíme až tím, že se budeme snažit vypsat hodnotu proměnné result:

user=> result
(Inkrementace cisla  1
Inkrementace cisla  2
2 Inkrementace cisla  3
3 Inkrementace cisla  4
4 5)

Jakmile jednou k vyhodnocení došlo, může Clojure již použít jednou vypočtené hodnoty výsledné sekvence:

user=> result
(2 3 4 5)
user=> 

Vzhledem k tomu, že se prvky sekvence získané makrem for vyhodnocují až ve chvíli, kdy jsou skutečně zapotřebí, nemusíme se bát zavolat toho makro i ve chvíli, kdy se na jeho vstupu objevuje nekonečná sekvence. Takovou sekvenci lze v Clojure asi nejjednodušeji získat zavoláním funkce range bez parametrů:

(user=> (def result (for [x (range)] (/ 1 (inc x))))
#'user/result

Pokud se nebudeme snažit získat všechny prvky výsledné nekonečné sekvence, ale spokojíme se pouze s vybraným konečným počtem prvků, bude vše v pořádku:

user=> (take 10 result)
(1 1/2 1/3 1/4 1/5 1/6 1/7 1/8 1/9 1/10)

5. Použití modifikátorů :while a :when v makru for

V makru for je možné kromě zadání jména lokální řídicí proměnné a zdrojové sekvence (či, jak uvidíme dále, několika sekvencí) používat i takzvané modifikátory :while a :when. Modifikátor :while slouží pro zastavení generování výsledné sekvence ve chvíli, kdy přestane platit zadaná podmínka. V následujícím demonstračním příkladu je sice zdrojová sekvence stoprvková, ovšem vytvořená sekvence bude mít pouze deset prvků:

user=> (for [x (range 100) :while (< x 10)] x)
(0 1 2 3 4 5 6 7 8 9)

Z výše uvedeného popisu modifikátoru :while vyplývá, že se nemusíme bát použít nekonečnou zdrojovou sekvenci, a to ani v případě, že se bude výsledná sekvence vyhodnocovat:

user=> (for [x (range) :while (< x 10)] (* x x))
(0 1 4 9 16 25 36 49 64 81)

Podmínka uvedená za modifikátorem :while může mít podobu téměř jakékoli formy (výrazu) vracející pravdivostní hodnotu true či false. V následujícím příkladu se vypíšou faktoriály menší než 1000:

user=> (for [x (range 100) :while (< (fact x) 1000)] (fact x))
(1 1 2 6 24 120 720)

Popř. si můžeme dovolit i tento zápis:

user=> (for [x (range) :while (< (fact x) 1000)] (fact x))
(1 1 2 6 24 120 720)

Druhý modifikátor se jmenuje :when a pracuje spíše jako filtr, tj. určuje, pro které hodnoty řídicí proměnné se má zavolat funkce specifikovaná v makru for:

user=> (for [x (range 100) :when (< x 10)] x)
(0 1 2 3 4 5 6 7 8 9)

Vzhledem k tomu, že :when pouze filtruje hodnoty, ale nezastavuje vytváření výsledné sekvence, musíme si dát pozor na nekonečné sekvence:

user=> (for [x (range) :when (< x 10)] x)
; nikdy neskončí

Někdy je však použití líně vyhodnocovaných nekonečných sekvencí možné, viz též tento příklad, kde se provedla ,„zarážka“ pomocí funkce take:

user=> (take 20 (for [x (range) :when (> (* x x) 3)] (* 2 x)))
(4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 42)

6. Práce s více generátory současně

V úvodní kapitole jsme si řekli, že makro for dokáže pracovat s více generátory (tj. s více zdrojovými sekvencemi) současně. Princip jeho práce se podobá funkci vnořených programových smyček typu „for“, samozřejmě s tím rozdílem, že makro for vyhodnocuje výslednou sekvenci líně. Podívejme se nyní, jak se vlastně makro for s více generátory zapisuje. Nejjednodušší způsob zápisu vypadá následovně:

user=> (for [x (range 1 10) y (range 1 10)] (* x y))
(1 2 3 4 5 6 7 8 9
2 4 6 8 10 12 14 16 18
3 6 9 12 15 18 21 24 27
4 8 12 16 20 24 28 32 36
5 10 15 20 25 30 35 40 45
6 12 18 24 30 36 42 48 54
7 14 21 28 35 42 49 56 63
8 16 24 32 40 48 56 64 72
9 18 27 36 45 54 63 72 81)
user=> 

Makro vytvořilo (línou) sekvenci obsahující 9×9=81 prvků, přičemž každý prvek byl získán vynásobením hodnoty dvou řídicích proměnných x a y, přičemž proměnná y byla měněná „rychleji“, tedy tak, jak jsme na to zvyklí i z vnořených programových smyček.

My ovšem můžeme taktéž vytvořit sekvenci obsahující dvojice, trojice atd. prvků. Postačuje jen vhodně upravit funkci volanou makrem for, například takovým způsobem, aby se vytvořit vektor obsahující dvojici čísel:

user=> (for [x (range 1 10) y (range 1 10)] [x y]) 
(
[1 1] [1 2] [1 3] [1 4] [1 5] [1 6] [1 7] [1 8] [1 9]
[2 1] [2 2] [2 3] [2 4] [2 5] [2 6] [2 7] [2 8] [2 9]
[3 1] [3 2] [3 3] [3 4] [3 5] [3 6] [3 7] [3 8] [3 9]
[4 1] [4 2] [4 3] [4 4] [4 5] [4 6] [4 7] [4 8] [4 9]
[5 1] [5 2] [5 3] [5 4] [5 5] [5 6] [5 7] [5 8] [5 9]
[6 1] [6 2] [6 3] [6 4] [6 5] [6 6] [6 7] [6 8] [6 9]
[7 1] [7 2] [7 3] [7 4] [7 5] [7 6] [7 7] [7 8] [7 9]
[8 1] [8 2] [8 3] [8 4] [8 5] [8 6] [8 7] [8 8] [8 9]
[9 1] [9 2] [9 3] [9 4] [9 5] [9 6] [9 7] [9 8] [9 9])
user=> 

Jednu sekvenci (seznam, množinu, vektor) lze v makru for použít i vícekrát, jednotlivé průchody sekvencí jsou na sobě nezávislé:

; sekvence použitá v makru for
user=> (def my-sequence (range 1 5))
#'user/my-sequence
 
; sekvenci použijeme celkem třikrát
user=> (for [x my-sequence y my-sequence z my-sequence] [x y z])
([1 1 1] [1 1 2] [1 1 3] [1 1 4]
 [1 2 1] [1 2 2] [1 2 3] [1 2 4]
 [1 3 1] [1 3 2] [1 3 3] [1 3 4]
 [1 4 1] [1 4 2] [1 4 3] [1 4 4]
 [2 1 1] [2 1 2] [2 1 3] [2 1 4]
 [2 2 1] [2 2 2] [2 2 3] [2 2 4]
 [2 3 1] [2 3 2] [2 3 3] [2 3 4]
 [2 4 1] [2 4 2] [2 4 3] [2 4 4]
 [3 1 1] [3 1 2] [3 1 3] [3 1 4]
 [3 2 1] [3 2 2] [3 2 3] [3 2 4]
 [3 3 1] [3 3 2] [3 3 3] [3 3 4]
 [3 4 1] [3 4 2] [3 4 3] [3 4 4]
 [4 1 1] [4 1 2] [4 1 3] [4 1 4]
 [4 2 1] [4 2 2] [4 2 3] [4 2 4]
 [4 3 1] [4 3 2] [4 3 3] [4 3 4]
 [4 4 1] [4 4 2] [4 4 3] [4 4 4])
user=>

Samozřejmě, že i v případě použití více generátorů je možné využít modifikátory :while a :when, například následujícím způsobem:

user=> (for [x (range 1 10) y (range 1 10) :while (< y x)] [x y])
(
[2 1]
[3 1] [3 2]
[4 1] [4 2] [4 3]
[5 1] [5 2] [5 3] [5 4]
[6 1] [6 2] [6 3] [6 4] [6 5]
[7 1] [7 2] [7 3] [7 4] [7 5] [7 6]
[8 1] [8 2] [8 3] [8 4] [8 5] [8 6] [8 7]
[9 1] [9 2] [9 3] [9 4] [9 5] [9 6] [9 7] [9 8]
)
user=>

Poznámka: výpis je samozřejmě upraven ručně, aby bylo patrné, jakým způsobem je výsledná sekvence vytvořena, přesněji řečeno, zda modifikátor :while ovlivnil vnitřní či vnější „smyčku“.

7. Demonstrační příklad – výpis hodnot hracích karet

Podívejme se nyní na možná poněkud „praktičtější“ demonstrační příklad, v němž máme za úkol vypsat všech 52 karet francouzského typu. Celý příklad je možné zapsat pomocí jediné formy, ovšem pro větší přehlednost bude lepší, když si nejprve vytvoříme dvojici zdrojových sekvencí (konkrétně vektorů), přičemž první sekvence bude obsahovat čtyři symboly na kartách (znaky v Unicode) a druhá sekvence pak hodnoty karet:

; sekvence (vektor) obsahující symboly na kartách
user=> (def card-symbols ['\u2660 '\u2665 '\u2666 '\u2663])
#'user/card-symbols
 
; sekvence (vektor) obsahující hodnoty karet
user=> (def card-numbers [2 3 4 5 6 7 8 9 10 'J 'Q 'K 'A])
#'user/card-numbers

Nyní již můžeme s využitím makra for získat sekvenci obsahující dvojice symbol:hodnota:

user=> (def result (for [card-symbol card-symbols card-number card-numbers] [card-symbol card-number]))
#'user/result

Následně je již možné karty vypsat, například aplikací funkce print na získanou sekvenci:

user=> (apply print result)
[♠ 2] [♠ 3] [♠ 4] [♠ 5] [♠ 6] [♠ 7] [♠ 8] [♠ 9] [♠ 10] [♠ J] [♠ Q] [♠ K] [♠ A]
[♥ 2] [♥ 3] [♥ 4] [♥ 5] [♥ 6] [♥ 7] [♥ 8] [♥ 9] [♥ 10] [♥ J] [♥ Q] [♥ K] [♥ A]
[♦ 2] [♦ 3] [♦ 4] [♦ 5] [♦ 6] [♦ 7] [♦ 8] [♦ 9] [♦ 10] [♦ J] [♦ Q] [♦ K] [♦ A]
[♣ 2] [♣ 3] [♣ 4] [♣ 5] [♣ 6] [♣ 7] [♣ 8] [♣ 9] [♣ 10] [♣ J] [♣ Q] [♣ K] [♣ A]
nil

8. Praktický příklad – výpočet Eulerova čísla s využitím makra for

Abychom si ukázali jednu z možností praktického využití makra for v aplikacích, napíšeme s jeho pomocí program pro výpočet přibližné hodnoty Eulerova čísla, které tvoří základ přirozených logaritmů a má v matematice jako jedna ze základních konstant i mnoho dalších použití. Pro výpočet Eulerova čísla použijeme součet nekonečné číselné řady prvků s hodnotou 1/x!. Celý vzorec lze nalézt na stránce http://cs.wikipedia.org/wi­ki/Eulerovo_číslo ve formě druhé definice této konstanty.

Aby bylo možné provést součet prvků výše zmíněné nekonečné řady, musíme nejprve vytvořit funkci pro výpočet faktoriálu. Některé způsoby zápisu této funkce jsme si již ukazovali v předchozích částech tohoto seriálu – jednalo se jak o čistě rekurzivní zápis, tak i o zápis používající tail rekurzi, popř. programovou smyčku. Dnes se pro jednoduchost spolehneme na použití funkce apply, která bude aplikovat operaci násobení na aritmetickou řadu celých čísel od 1 do n (jedná se o pravděpodobně nejkratší zápis faktoriálu):

user=> (defn fact [n] (apply * (range 1 (inc n))))
#'user/fact

Námi vytvořenou funkci pro výpočet faktoriálu můžeme jednoduše otestovat, například její aplikací na vstupní hodnoty 0..10:

user=> (map fact (range 1 10))
(1 2 6 24 120 720 5040 40320 362880)

Funkci fact můžeme použít společně s makrem for pro vytvoření číselné řady obsahující hodnoty 1/x!, přičemž x leží ve zvoleném rozsahu (0..max):

user=> (for [x (range 0 10)] (/ 1 (fact x)))
(1 1 1/2 1/6 1/24 1/120 1/720 1/5040 1/40320 1/362880)

Pro výpočet přibližné hodnoty Eulerova čísla nám již pouze postačí výsledné hodnoty sečíst, například takto:

user=> (apply + (for [x (range 0 10)] (/ 1 (fact x))))
98641/36288

Zlomek, tj. numerická hodnota typu ratio, se na desetinné číslo převede s využitím konverzní funkce double:

user=> (double (apply + (for [x (range 0 10)] (/ 1 (fact x)))))
2.718281525573192

Tato hodnota již leží poměrně blízko k očekávané hodnotě konstanty e:

docker + kubernetes školení s dotací tip

user=> (Math/E)
2.718281828459045

Při požadavku na vyšší přesnost výpočtu stačí jednoduše zvýšit počet členů řady, tj. upravit druhý parametr funkce (range).

Předchozí fragment programu pro výpočet Eulerova čísla si můžeme pro větší čitelnost rozepsat:

(defn fact
    [n]
    (apply *
        (range 1 (inc n))))
 
(double
    (apply +
        (for
            [x (range 0 10)]
            (/ 1 (fact x)))))

9. Odkazy na Internetu

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