Hlavní navigace

Programovací jazyk Clojure – práce s mapami a množinami

Pavel Tišnovský 11. 8. 2015

Po vysvětlení principu práce se sekvencemi, seznamy a vektory v programovacím jazyku Clojure nám zbývá popis zbylých dvou strukturovaných datových typů. Jedná se o velmi důležité mapy (maps), které mají v Clojure hned několik využití (i implementací), a dále pak o množiny (sets).

Obsah

1. Programovací jazyk Clojure – práce s mapami a množinami

2. Typy map v Clojure

3. Implementace clojure.lang.PersistentHashMap

4. INode

5. ArrayNode a BitmapIndexedNode

6. Operace s mapami: assoc a dissoc

7. Vyhledávání prvků, získání klíčů a hodnot

8. Operace merge a zipmap

9. Práce s množinami v jazyce Clojure

10. Množinové operace

11. Odkazy na předchozí části tohoto seriálu

12. Odkazy na Internetu

1. Programovací jazyk Clojure – práce s mapami a množinami

Ve dvacáté první části seriálu o programovacím jazyku Clojure i o knihovnách, které jsou pro tento jazyk dostupné, jsme se zaměřili na popis práce se seznamy a vektory. Jedná se o základní strukturované datové typy, které jsou v Clojure používané ve všech programech. Díky homoikonicitě se navíc seznamy a vektory používají i přímo pro zápis algoritmů – každá definice, funkce či makro jsou reprezentovány stromem sestrojeným z vnořených seznamů a vektorů (vektory jsou použity například pro deklaraci parametrů, najdeme je ve speciální formě let, v makru for atd.). Ostatně právě tato vlastnost jazyka Clojure se – stejně jako u LISPu – velmi často využívá při tvorbě maker, protože se stromovou reprezentací funkce je možné poměrně snadným způsobem manipulovat.

Nesmíme však zapomenout ani na zbylé dva strukturované datové typy. Jedná se o mapy (maps) a množiny (sets). Především mapy jsou velmi důležité v mnoha aplikacích naprogramovaných v Clojure, neboť se s jejich využitím mohou vytvářet struktury/záznamy (structs/records), hodnotové objekty atd. Mapy se velmi často používají pro předávání pojmenovaných parametrů do volaných funkcí či maker atd. Navíc jsou mapy zajímavé i z toho hlediska jejich implementace.

2. Typy map v Clojure

Nejprve se budeme zabývat mapami. Ty existují ve dvou variantách, jejichž základní vlastnosti shrnuje tabulka zobrazená pod tímto odstavcem:

# Vlastnost Hash map Sorted map
1 konstruktor (hash-map …) (sorted-map …)
2 „literál“ {klíč hodnota klíč hodnota…} ×
2 složitost přístupu k prvkům O(log32N) O(N)
3 složitost (count) O(1) O(1)
4 setříděné prvky ne ano
5 podpora (seq) ano ano
6 podpora (rseq) ne ano
6 klíče nil ano ano
7 hodnoty nil ano ano

3. Implementace clojure.lang.PersistentHashMap

Hash mapy se v praxi používají (minimálně v případě programovacího jazyka Clojure) mnohem častěji než mapy, v nichž jsou prvky setříděny na základě hodnoty svého klíče. Proto si podrobněji popíšeme interní strukturu hash mapy. Jedná se o neměnitelnou (immutable) a perzistentní (persistent) datovou strukturu, což znamená, že do jednou vytvořené mapy již nelze přidávat další prvky (dvojice klíč-hodnota) ani žádné prvky ubírat. Na druhou stranu však mapy mohou sdílet svoji interní strukturu, takže přidání nového prvku lze efektivně zařídit vytvořením nové mapy s přidaným prvkem (což zajišťuje funkce nazvaná assoc, kterou si popíšeme níže). Hash mapy jsou interně uloženy podobným způsobem jako minule popsané vektory, tj. s využitím stromu, jehož každý uzel může obsahovat až 32 odkazů na další poduzly. Takové stromy jsou typicky velmi nízké a tím pádem je i jejich prohledání rychlé – složitost je O(log32N), tj. „prakticky konstantní“.

4. INode

Interně je hash mapa tvořena stromem obsahujícím jako své uzly objekty objekty s rozhraním INode (tj. jedná se o instance tříd implementujících toto rozhraní). Samotná struktura INode se postupně vyvíjí a mění. V současné verzi jazyka Clojure (1.6.0) vypadá následovně:

static interface INode extends Serializable {
        INode assoc(int shift, int hash, Object key, Object val, Box addedLeaf);
        INode without(int shift, int hash, Object key);
        IMapEntry find(int shift, int hash, Object key);
        Object find(int shift, int hash, Object key, Object notFound);
        ISeq nodeSeq();
        INode assoc(AtomicReference<Thread> edit, int shift, int hash, Object key, Object val, Box addedLeaf);
        INode without(AtomicReference<Thread> edit, int shift, int hash, Object key, Box removedLeaf);
        public Object kvreduce(IFn f, Object init);
        Object fold(IFn combinef, IFn reducef, IFn fjtask, IFn fjfork, IFn fjjoin);
        Iterator iterator(IFn f);
}

V minulosti existovalo pět tříd implementujících rozhraní INode, v moderním Clojure jsou to již jen tři třídy: ArrayNode, BitmapIndexedNode a HashCollisionNode.

5. ArrayNode a BitmapIndexedNode

Třída ArrayNode interně obsahuje pole INodu a hodnotu obsahující počet aktivních prvků v poli. Důležité je, že zde není uložen přímo klíč a hodnota prvku, jen reference na INode popř. hodnota null. Operace assoc (přidání prvku se strukturálním sdílením) znamená buď pouhé přidání nového prvku nebo klonování pole s následným přidáním (klonují se ovšem pouze reference).

Mnohem zajímavější je třída BitmapIndexedMode. Zde je interně použito pole Object[] o kapacitě 32 prvků. Význam jednotlivých položek se může měnit. Sudé prvky obsahují buď hodnotu null nebo klíč, liché prvky buď hodnotu (navázanou na klíč) nebo referenci na další INode (což je rekurzivně opět ArrayNode či BitmapIndexedMode):

  1. Když pole[2*i]==null pak pole[2*i+1] je INode
  2. Když pole[2*i]!=null pak pole[2*i] je klíč a pole[2*i+1] je hodnota

To znamená, že zmíněné 32prvkové pole může maximálně obsahovat 16 prvků klíč+hodnota, 32 odkazů na poduzly typu INode nebo mix obou typů záznamů.

Ve skutečnosti se pracuje s klíčem nepřímo přes jeho hash kód zjištěný funkcí hash (jde o 32bitovou hodnotu).

6. Operace s mapami: assoc a dissoc

Mezi dvě základní operace, které je možné provádět s mapami, patří operace reprezentované funkcemi nazvanými assoc a dissoc. První z těchto funkcí přidá k již existující mapě novou dvojici klíč-hodnota (či několik dvojic klíč-hodnota) a vrátí novou mapu rozšířenou o tyto prvky (mapy jsou totiž neměnné datové typy, podobně jako seznamy a vektory). Druhá funkce – dissoc – plní opačnou roli, protože z nějaké mapy odstraní dvojici/e klíč-hodnota pro zadaný klíč či několik klíčů (jediným voláním dissoc je tedy možné odstranit více prvků). I tato funkce nemění původní mapu, ale vrací namísto toho novou datovou strukturu, která ovšem s původní mapou většinu prvků sdílí. Ukažme si chování obou zmíněných funkcí assoc a dissoc na jednoduchém příkladu:

Nejprve vytvoříme mapu, jejímiž klíči jsou symboly a hodnotami řetězce (zejména použití symbolů pro klíče je v Clojure idiomatické):

user=> (def client {:id 42
  #_=>              :name "Sheldon"
  #_=>              :surname "Cooper"
  #_=>              :real-name "Jim Parsons"})
#'user/client

Zobrazíme obsah této mapy (povšimněte si, že prvky jsou zobrazeny náhodně, protože se jedná o hashmapu:

user=> client
{:name "Sheldon", :surname "Cooper", :id 42, :real-name "Jim Parsons"}

Funkcí assoc vytvoříme novou mapu, do níž bude přidán další prvek:

user=> (assoc client :iq 187)
{:name "Sheldon", :surname "Cooper", :iq 187, :id 42, :real-name "Jim Parsons"}

Původní mapa zůstala nezměněna:

user=> client
{:name "Sheldon", :surname "Cooper", :id 42, :real-name "Jim Parsons"}

Funkcí dissoc vytvoříme mapu s odstraněným jedním prvkem či dvěma prvky:

user=> (dissoc client :surname)
{:name "Sheldon", :id 42, :real-name "Jim Parsons"}
 
user=> (dissoc client :name :surname)
{:id 42, :real-name "Jim Parsons"}

Opět se můžeme přesvědčit o tom, že původní mapa zůstala nezměněna:

user=> client
{:name "Sheldon", :surname "Cooper", :id 42, :real-name "Jim Parsons"}

Poznámka: minule jsme se seznámili s takzvanými dočasnými (tranzientními) datovými typy. I pro mapy existuje tranzientní ekvivalent, pro nějž jsou definovány funkce assoc! a dissoc! (s vykřičníkem na konci). Výsledné hodnoty těchto funkcí jsou opět tranzientními mapami, které je možné v případě potřeby navázat na symbol původní tranzientní mapy.

7. Vyhledávání prvků, získání klíčů a hodnot

Mezi další důležité operace, které se s mapami většinou provádí, patří zjištění, zda mapa obsahuje prvek (tj. klíč-hodnota). Pro tento účel nám Clojure nabízí hned několik funkcí. Opět si tyto funkce nejlépe odzkoušíme na jednoduchých příkladech.

Funkce budou otestovány na mapě, jejímiž klíči jsou římské číslice a hodnotami odpovídající číslice arabské:

user=> (def numbers {"I" 1 "II" 2 "III" 3 "IV" 4 "V" 5})
#'user/numbers

Zobrazíme obsah této mapy (povšimněte si, že prvky jsou zobrazeny náhodně, protože se jedná o hashmapu:

user=> (println numbers)
{III 3, II 2, V 5, I 1, IV 4}
nil

První funkcí je predikát contains?, kde otazník naznačuje, že se vždy vrací pravdivostní hodnota true či false v závislosti na tom, zda v mapě existuje prvek s daným klíčem:

user=> (contains? numbers "I")
true
 
user=> (contains? numbers 1)
false
 
user=> (contains? numbers "xyzzy")
false

Druhá důležitá funkce je get, která vrací hodnotu navázanou na zadaný klíč popř. nil, pokud klíč nebyl nalezen. Pokud se do mapy ukládají i hodnoty nil, nezbývá většinou nic jiného, než kombinace get+contains?:

user=> (get numbers "V")
5
 
user=> (get numbers "xyzzy")
nil

Funkce find je podobná funkci get, ale namísto hodnoty navázané na klíč vrací dvojici klíč+hodnota:

user=> (find numbers "V")
["V" 5]
 
user=> (find numbers "xyzzy")
nil
 
user=> (type (find numbers "V"))
clojure.lang.MapEntry

Získat je možné všechny klíče, všechny hodnoty či provést selekci na základě sekvence klíčů:

user=> (keys numbers)
("III" "II" "V" "I" "IV")
 
user=> (vals numbers)
(3 2 5 1 4)
 
user=> (select-keys numbers ["I" "IV" "V"])
{"V" 5, "IV" 4, "I" 1}
 
user=> (select-keys numbers ["I" "IV" "neznamy"])
{"IV" 4, "I" 1}

Výběr jednoho prvku je možné provést i tím, že se zapíše volání (klíč mapa), což je v Clojure idiomatické:

user=> (def numbers1 {:I 1 :II 2 :III 3 :IV 4 :V 5 :VI 6})
#'user/numbers1
 
user=> (:III numbers1)
3

Podívejme se na praktičtější příklad používající mapu namísto struktury/záznamu a dvojí způsob získání jedné hodnoty na základě klíče:

user=> (def client {:id 42
  #_=>              :name "Sheldon"
  #_=>              :surname "Cooper"
  #_=>              :real-name "Jim Parsons"})
#'user/client
 
user=> client
{:name "Sheldon", :surname "Cooper", :id 42, :real-name "Jim Parsons"}
 
user=> (get client :name)
"Sheldon"
 
user=> (:name client)
"Sheldon"

8. Operace merge a zipmap

Posledními dvěma důležitými operacemi, s nimiž se lze v reálných aplikacích velmi často setkat, jsou operace reprezentované funkcemi merge a zipmap. Funkce nazvaná merge slouží ke spojení dvou či většího množství map. Nejprve opět použijeme známou strukturu obsahující základní informace o Sheldonovi:

user=> (def client {:id 42
  #_=>              :name "Sheldon"
  #_=>              :surname "Cooper"
  #_=>              :real-name "Jim Parsons"})
#'user/client
 
user=> client
{:name "Sheldon", :surname "Cooper", :id 42, :real-name "Jim Parsons"}

Nyní zavoláme funkci merge, jejímž výsledkem bude nová mapa:

user=> (merge client {:iq 187 :degrees ["Ph.D." "Sc.D."]})
{:name "Sheldon", :surname "Cooper", :iq 187, :id 42, :degrees ["Ph.D." "Sc.D."], :real-name "Jim Parsons"}

Původní mapa zůstala nezměněna:

user=> client
{:name "Sheldon", :surname "Cooper", :id 42, :real-name "Jim Parsons"}

Lépe bude vidět význam funkce merge u map se shodnými klíči. Při spojování v takovém případě „vyhraje“ hodnota uložená v poslední mapě vstupující do merge:

user=> (def numbers1 {"I" 1 "II" 2 "III" 3 "IV" 4 "V" 5 "VI" 6})
#'user/numbers1
 
user=> (def numbers2 {"VI" "sest" "VII" 7 "VIII" 8 "IX" 9 "X" 9})
#'user/numbers2
 
user=> (merge numbers1 numbers2)
{"III" 3, "VIII" 8, "II" 2, "V" 5, "VII" 7, "X" 9, "VI" "sest", "IX" 9, "I" 1, "IV" 4}

Ovšem:

user=> (merge numbers2 numbers1)
{"III" 3, "VIII" 8, "II" 2, "V" 5, "VII" 7, "X" 9, "VI" 6, "IX" 9, "I" 1, "IV" 4}

Velmi užitečná je v praxi funkce nazvaná zipmap, která umožňuje zkombinovat dvě sekvence do jediné mapy. První sekvence obsahuje klíče, druhá sekvence hodnoty. Můžeme tedy psát například:

user=> (zipmap ["I" "II" "III" "IV" "V" "VI"]
  #_=>         [1 2 3 4 5 6])
{"VI" 6, "V" 5, "IV" 4, "III" 3, "II" 2, "I" 1}

Pravověrný Clojure programátor by ovšem napsal (se stejným výsledkem):

user=> (zipmap ["I" "II" "III" "IV" "V" "VI"]
  #_=>         (range 1 7))
{"VI" 6, "V" 5, "IV" 4, "III" 3, "II" 2, "I" 1}

Pozor ovšem na to, že zipmap nejde jednoduše použít s nekonečnými lazy sekvencemi. Toto není dobrý nápad:

(zipmap (range) (repeat 42))

(Stačí ovšem, aby první sekvence byla konečná).

9. Práce s množinami v jazyce Clojure

Poslední důležitou datovou strukturou, se kterou se dříve či později musí seznámit jakýkoli programátor používající jazyk Clojure, jsou množiny (sets), které jsou charakteristické tím, že každý prvek obsahují maximálně jednou (na rozdíl od seznamů a vektorů). Podobně jako mapy, i množiny existují ve dvou podobách: s nesetříděnými prvky (hash set) a setříděnými prvky (sorted set). Pro vytvoření prvního typu množiny je možné použít buď „literál“ s formou zápisu #{prvek1 prvek2 prvek3} nebo funkci-konstruktor nazvanou hash-set. Pro vytvoření druhého prvku množiny se používá výhradně konstruktoru představovaného funkcí sorted-set. Nejdůležitějšími množinovými operacemi je sjednocení, průnik, rozdíl (viz též další kapitolu) a test, zda je jedna množina podmnožinou či nadmnožinou jiné množiny. V Clojure je ovšem množiny možné využít podobně jako další typy sekvencí:

user=> (def colors #{:red :green :blue :yellow :magenta :cyan})
#'user/colors
 
user=> (count colors)
6
 
user=> (:red colors)
:red
 
user=> (:xxx colors)
nil
 
user=> (get colors :red)
:red
 
user=> (get colors :xxx)
nil
 
user=> (contains? colors :red)
true
 
user=> (contains? colors :xxx)
false

Fungovat bude i přiřazení kódu (indexu) každé barvě:

user=> (zipmap (range) colors)
{5 :magenta, 4 :blue, 3 :red, 2 :cyan, 1 :green, 0 :yellow}

10. Množinové operace

Podívejme se nyní na vybrané operace (funkce), které je možné aplikovat na množiny. Nejprve vytvoříme trojici množin, z nichž první reprezentuje množinu s prvky černá a bílá, druhá množina obsahuje prvky červená, zelená, modrá a třetí množina obsahuje prvky azurová, fialová, modrá:

user=> (def bw #{:white :black})
#'user/bw
 
user=> (def rgb #{:red :green :blue})
#'user/rgb
 
user=> (def cmy #{:cyan :magenta :yellow})
#'user/cmy

Obsah všech tří množin si vypíšeme:

user=> bw
#{:white :black}
 
user=> rgb
#{:green :red :blue}
 
user=> cmy
#{:yellow :cyan :magenta}

S využitím operace union vytvoříme dvě nové množiny nazvané „cmyk“ a „palette“. První z těchto množin bude obsahovat prvky azurová, fialová, modrá, druhá množina pak všech osm prvků představujících základní barvy ZX Spectra či textových režimů PC:

user=> (def cmyk (clojure.set/union cmy #{:black}))
#'user/cmyk
 
user=> cmyk
#{:yellow :cyan :magenta :black}
 
user=> (def palette (clojure.set/union bw cmy rgb))
#'user/palette
 
user=> palette
#{:white :yellow :green :cyan :red :blue :magenta :black}

Komu se nechce počet barev palety počítat, může použít:

user=> (count palette)
8

Nyní otestujeme operaci intersection (průnik):

user=> (clojure.set/intersection cmyk rgb)
#{}
 
user=> (clojure.set/intersection cmyk bw)
#{:black}

Na závěr zjistíme, zda je některá množina podmnožinou či nadmnožinou jiné množiny:

user=> (clojure.set/subset? rgb palette)
true
 
user=> (clojure.set/subset? rgb cmyk)
false
 
user=> (clojure.set/subset? cmy cmyk)
true
 
user=> (clojure.set/superset? cmy cmyk)
false
 
user=> (clojure.set/superset? cmyk cmy)
true

11. Odkazy na předchozí části tohoto seriálu

  1. Leiningen: nástroj pro správu projektů napsaných v Clojure
    http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure/
  2. Leiningen: nástroj pro správu projektů napsaných v Clojure (2)
    http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-2/
  3. Leiningen: nástroj pro správu projektů napsaných v Clojure (3)
    http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-3/
  4. Leiningen: nástroj pro správu projektů napsaných v Clojure (4)
    http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-4/
  5. Leiningen: nástroj pro správu projektů napsaných v Clojure (5)
    http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-5/
  6. Leiningen: nástroj pro správu projektů napsaných v Clojure (6)
    http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-6/
  7. Programovací jazyk Clojure a databáze (1.část)
    http://www.root.cz/clanky/programovaci-jazyk-clojure-a-databaze-1-cast/
  8. Pluginy pro Leiningen
    http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-pluginy-pro-leiningen/
  9. Programovací jazyk Clojure a knihovny pro práci s vektory a maticemi
    http://www.root.cz/clanky/programovaci-jazyk-clojure-a-knihovny-pro-praci-s-vektory-a-maticemi/
  10. Programovací jazyk Clojure a knihovny pro práci s vektory a maticemi (2)
    http://www.root.cz/clanky/programovaci-jazyk-clojure-a-knihovny-pro-praci-s-vektory-a-maticemi-2/
  11. Programovací jazyk Clojure: syntéza procedurálních textur s využitím knihovny Clisk
    http://www.root.cz/clanky/programovaci-jazyk-clojure-synteza-proceduralnich-textur-s-vyuzitim-knihovny-clisk/
  12. Programovací jazyk Clojure: syntéza procedurálních textur s využitím knihovny Clisk (2)
    http://www.root.cz/clanky/programovaci-jazyk-clojure-synteza-proceduralnich-textur-s-vyuzitim-knihovny-clisk-2/
  13. Programovací jazyk Clojure: syntéza procedurálních textur s využitím knihovny Clisk (dokončení)
    http://www.root.cz/clanky/programovaci-jazyk-clojure-synteza-proceduralnich-textur-s-vyuzitim-knihovny-clisk-dokonceni/
  14. Seesaw: knihovna pro snadnou tvorbu GUI v jazyce Clojure
    http://www.root.cz/clanky/seesaw-knihovna-pro-snadnou-tvorbu-gui-v-jazyce-clojure/
  15. Seesaw: knihovna pro snadnou tvorbu GUI v jazyce Clojure (2)
    http://www.root.cz/clanky/seesaw-knihovna-pro-snadnou-tvorbu-gui-v-jazyce-clojure-2/
  16. Seesaw: knihovna pro snadnou tvorbu GUI v jazyce Clojure (3)
    http://www.root.cz/clanky/seesaw-knihovna-pro-snadnou-tvorbu-gui-v-jazyce-clojure-3/
  17. Programovací jazyk Clojure a práce s Gitem
    http://www.root.cz/clanky/programovaci-jazyk-clojure-a-prace-s-gitem/
  18. Programovací jazyk Clojure a práce s Gitem (2)
    http://www.root.cz/clanky/programovaci-jazyk-clojure-a-prace-s-gitem-2/
  19. Programovací jazyk Clojure – triky při práci s řetězci
    http://www.root.cz/clanky/programovaci-jazyk-clojure-triky-pri-praci-s-retezci/
  20. Programovací jazyk Clojure – triky při práci s kolekcemi
    http://www.root.cz/clanky/programovaci-jazyk-clojure-triky-pri-praci-s-kolekcemi/

12. Odkazy na Internetu

  1. Understanding Clojure's PersistentVector implementation
    http://blog.higher-order.net/2009/02/01/understanding-clojures-persistentvector-implementation
  2. Understanding Clojure's PersistentHashMap (deftwice…)
    http://blog.higher-order.net/2009/09/08/understanding-clojures-persistenthashmap-deftwice.html
  3. Assoc and Clojure's PersistentHashMap: part ii
    http://blog.higher-order.net/2010/08/16/assoc-and-clojures-persistenthashmap-part-ii.html
  4. Ideal Hashtrees (paper)
    http://lampwww.epfl.ch/pa­pers/idealhashtrees.pdf
  5. Clojure home page
    http://clojure.org/
  6. Clojure (downloads)
    http://clojure.org/downloads
  7. Clojure Sequences
    http://clojure.org/sequences
  8. Clojure Data Structures
    http://clojure.org/data_structures
  9. The Structure and Interpretation of Computer Programs: 2.2.1 Representing Sequences
    http://mitpress.mit.edu/sicp/full-text/book/book-Z-H-15.html#%_sec2.2.1
  10. The Structure and Interpretation of Computer Programs: 3.3.1 Mutable List Structure
    http://mitpress.mit.edu/sicp/full-text/book/book-Z-H-22.html#%_sec3.3.1
  11. Clojure – Functional Programming for the JVM
    http://java.ociweb.com/mar­k/clojure/article.html
  12. Clojure quick reference
    http://faustus.webatu.com/clj-quick-ref.html
  13. 4Clojure
    http://www.4clojure.com/
  14. ClojureDoc (rozcestník s dokumentací jazyka Clojure)
    http://clojuredocs.org/
  15. Clojure (na Wikipedia EN)
    http://en.wikipedia.org/wiki/Clojure
  16. Clojure (na Wikipedia CS)
    http://cs.wikipedia.org/wiki/Clojure
  17. SICP (The Structure and Interpretation of Computer Programs)
    http://mitpress.mit.edu/sicp/
  18. Pure function
    http://en.wikipedia.org/wi­ki/Pure_function
  19. Funkcionální programování
    http://cs.wikipedia.org/wi­ki/Funkcionální_programová­ní
  20. Čistě funkcionální (datové struktury, jazyky, programování)
    http://cs.wikipedia.org/wi­ki/Čistě_funkcionální
  21. Clojure Macro Tutorial (Part I, Getting the Compiler to Write Your Code For You)
    http://www.learningclojure­.com/2010/09/clojure-macro-tutorial-part-i-getting.html
  22. Clojure Macro Tutorial (Part II: The Compiler Strikes Back)
    http://www.learningclojure­.com/2010/09/clojure-macro-tutorial-part-ii-compiler.html
  23. Clojure Macro Tutorial (Part III: Syntax Quote)
    http://www.learningclojure­.com/2010/09/clojure-macro-tutorial-part-ii-syntax.html
  24. Tech behind Tech: Clojure Macros Simplified
    http://techbehindtech.com/2010/09/28/clo­jure-macros-simplified/
  25. Fatvat – Exploring functional programming: Clojure Macros
    http://www.fatvat.co.uk/2009/02/clo­jure-macros.html
  26. Eulerovo číslo
    http://cs.wikipedia.org/wi­ki/Eulerovo_číslo
  27. List comprehension
    http://en.wikipedia.org/wi­ki/List_comprehension
  28. List Comprehensions in Clojure
    http://asymmetrical-view.com/2008/11/18/list-comprehensions-in-clojure.html
  29. Clojure Programming Concepts: List Comprehension
    http://en.wikibooks.org/wi­ki/Clojure_Programming/Con­cepts#List_Comprehension
  30. Clojure core API: for macro
    http://clojure.github.com/clo­jure/clojure.core-api.html#clojure.core/for
  31. cirrus machina – The Clojure for macro
    http://www.cirrusmachina.com/blog/com­ment/the-clojure-for-macro/
  32. Riastradh's Lisp Style Rules
    http://mumble.net/~campbe­ll/scheme/style.txt
  33. Dynamic Languages Strike Back
    http://steve-yegge.blogspot.cz/2008/05/dynamic-languages-strike-back.html
  34. Scripting: Higher Level Programming for the 21st Century
    http://www.tcl.tk/doc/scripting.html
  35. Java Virtual Machine Support for Non-Java Languages
    http://docs.oracle.com/ja­vase/7/docs/technotes/gui­des/vm/multiple-language-support.html
  36. Třída java.lang.String
    http://docs.oracle.com/ja­vase/7/docs/api/java/lang/Strin­g.html
  37. Třída java.lang.StringBuffer
    http://docs.oracle.com/ja­vase/7/docs/api/java/lang/Strin­gBuffer.html
  38. Třída java.lang.StringBuilder
    http://docs.oracle.com/ja­vase/7/docs/api/java/lang/Strin­gBuilder.html
  39. StringBuffer versus String
    http://www.javaworld.com/ar­ticle/2076072/build-ci-sdlc/stringbuffer-versus-string.html
  40. Threading macro (dokumentace k jazyku Clojure)
    https://clojure.github.io/clo­jure/clojure.core-api.html#clojure.core/->
  41. Understanding the Clojure → macro
    http://blog.fogus.me/2009/09/04/un­derstanding-the-clojure-macro/
  42. clojure.inspector
    http://clojure.github.io/clo­jure/clojure.inspector-api.html
  43. The Clojure Toolbox
    http://www.clojure-toolbox.com/
  44. Unit Testing in Clojure
    http://nakkaya.com/2009/11/18/unit-testing-in-clojure/
  45. Testing in Clojure (Part-1: Unit testing)
    http://blog.knoldus.com/2014/03/22/tes­ting-in-clojure-part-1-unit-testing/
  46. API for clojure.test – Clojure v1.6 (stable)
    https://clojure.github.io/clo­jure/clojure.test-api.html
  47. Leiningen: úvodní stránka
    http://leiningen.org/
  48. Leiningen: Git repository
    https://github.com/techno­mancy/leiningen
  49. leiningen-win-installer
    http://leiningen-win-installer.djpowell.net/
  50. Clojure 1: Úvod
    http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm/
  51. Clojure 2: Symboly, kolekce atd.
    http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm-2-cast/
  52. Clojure 3: Funkcionální programování
    http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm-3-cast-funkcionalni-programovani/
  53. Clojure 4: Kolekce, sekvence a lazy sekvence
    http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm-4-cast-kolekce-sekvence-a-lazy-sekvence/
  54. Clojure 5: Sekvence, lazy sekvence a paralelní programy
    http://www.root.cz/clanky/clojure-a-bezpecne-aplikace-pro-jvm-sekvence-lazy-sekvence-a-paralelni-programy/
  55. Clojure 6: Podpora pro paralelní programování
    http://www.root.cz/clanky/programovaci-jazyk-clojure-6-futures-nejsou-jen-financni-derivaty/
  56. Clojure 7: Další funkce pro paralelní programování
    http://www.root.cz/clanky/programovaci-jazyk-clojure-7-dalsi-podpurne-prostredky-pro-paralelni-programovani/
  57. Clojure 8: Identity, stavy, neměnné hodnoty a reference
    http://www.root.cz/clanky/programovaci-jazyk-clojure-8-identity-stavy-nemenne-hodnoty-a-referencni-typy/
  58. Clojure 9: Validátory, pozorovatelé a kooperace s Javou
    http://www.root.cz/clanky/programovaci-jazyk-clojure-9-validatory-pozorovatele-a-kooperace-mezi-clojure-a-javou/
  59. Clojure 10: Kooperace mezi Clojure a Javou
    http://www.root.cz/clanky/programovaci-jazyk-clojure-10-kooperace-mezi-clojure-a-javou-pokracovani/
  60. Clojure 11: Generátorová notace seznamu/list comprehension
    http://www.root.cz/clanky/programovaci-jazyk-clojure-11-generatorova-notace-seznamu-list-comprehension/
  61. Clojure 12: Překlad programů z Clojure do bajtkódu JVM I:
    http://www.root.cz/clanky/programovaci-jazyk-clojure-12-preklad-programu-z-clojure-do-bajtkodu-jvm/
  62. Clojure 13: Překlad programů z Clojure do bajtkódu JVM II:
    http://www.root.cz/clanky/programovaci-jazyk-clojure-13-preklad-programu-z-clojure-do-bajtkodu-jvm-pokracovani/
  63. Clojure 14: Základy práce se systémem maker
    http://www.root.cz/clanky/programovaci-jazyk-clojure-14-zaklady-prace-se-systemem-maker/
  64. Clojure 15: Tvorba uživatelských maker
    http://www.root.cz/clanky/programovaci-jazyk-clojure-15-tvorba-uzivatelskych-maker/
  65. Clojure 16: Složitější uživatelská makra
    http://www.root.cz/clanky/programovaci-jazyk-clojure-16-slozitejsi-uzivatelska-makra/
  66. Clojure 17: Využití standardních maker v praxi
    http://www.root.cz/clanky/programovaci-jazyk-clojure-17-vyuziti-standardnich-maker-v-praxi/
  67. Clojure 18: Základní techniky optimalizace aplikací
    http://www.root.cz/clanky/programovaci-jazyk-clojure-18-zakladni-techniky-optimalizace-aplikaci/
  68. Clojure 19: Vývojová prostředí pro Clojure
    http://www.root.cz/clanky/programovaci-jazyk-clojure-19-vyvojova-prostredi-pro-clojure/
  69. Clojure 20: Vývojová prostředí pro Clojure (Vimu s REPL)
    http://www.root.cz/clanky/programovaci-jazyk-clojure-20-vyvojova-prostredi-pro-clojure-integrace-vimu-s-repl/
  70. Clojure 21: ClojureScript aneb překlad Clojure do JS
    http://www.root.cz/clanky/programovaci-jazyk-clojure-21-clojurescript-aneb-preklad-clojure-do-javascriptu/
  71. Clojure.org: Vars and the Global Environment
    http://clojure.org/Vars
  72. Clojure.org: Refs and Transactions
    http://clojure.org/Refs
  73. Clojure.org: Atoms
    http://clojure.org/Atoms
  74. Clojure.org: Agents as Asynchronous Actions
    http://clojure.org/agents
  75. Transient Data Structureshttp://clojure.or­g/transients
Našli jste v článku chybu?
Vitalia.cz: „Připluly“ z Německa a možná obsahují jed

„Připluly“ z Německa a možná obsahují jed

Podnikatel.cz: Přehledná titulka, průvodci, responzivita

Přehledná titulka, průvodci, responzivita

DigiZone.cz: Česká televize mění schéma ČT :D

Česká televize mění schéma ČT :D

Měšec.cz: Finančním poradcům hrozí vracení provizí

Finančním poradcům hrozí vracení provizí

Podnikatel.cz: Prodává přes internet. Kdy platí zdravotko?

Prodává přes internet. Kdy platí zdravotko?

Lupa.cz: Co se dá měřit přes Internet věcí

Co se dá měřit přes Internet věcí

Lupa.cz: Proč firmy málo chrání data? Chovají se logicky

Proč firmy málo chrání data? Chovají se logicky

Lupa.cz: UX přestává pro firmy být magie

UX přestává pro firmy být magie

Vitalia.cz: Jsou čajové sáčky toxické?

Jsou čajové sáčky toxické?

120na80.cz: Pánové, pečujte o svoje přirození a prostatu

Pánové, pečujte o svoje přirození a prostatu

Lupa.cz: Avast po spojení s AVG propustí 700 lidí

Avast po spojení s AVG propustí 700 lidí

Měšec.cz: Jak vymáhat výživné zadarmo?

Jak vymáhat výživné zadarmo?

Podnikatel.cz: EET zvládneme, budou horší zákony

EET zvládneme, budou horší zákony

Vitalia.cz: Láska na vozíku: Přitažliví jsme pro tzv. pečovatelky

Láska na vozíku: Přitažliví jsme pro tzv. pečovatelky

Podnikatel.cz: EET: Totálně nezvládli metodologii projektu

EET: Totálně nezvládli metodologii projektu

Měšec.cz: Kdy vám stát dá na stěhování 50 000 Kč?

Kdy vám stát dá na stěhování 50 000 Kč?

Podnikatel.cz: Na poslední chvíli šokuje vyjímkami v EET

Na poslední chvíli šokuje vyjímkami v EET

Měšec.cz: U levneELEKTRO.cz už reklamaci nevyřídíte

U levneELEKTRO.cz už reklamaci nevyřídíte

Podnikatel.cz: 1. den EET? Problémy s pokladnami

1. den EET? Problémy s pokladnami

DigiZone.cz: Sony KD-55XD8005 s Android 6.0

Sony KD-55XD8005 s Android 6.0