Hlavní navigace

Interpret programovacího jazyka Clojure integrovaný do Jupyter Notebooku

Jazyk Clojure je, podobně jako většina LISPovských jazyků, vybaven interaktivní smyčkou REPL. Existuje i interaktivní prostředí Gorilla s GUI, podporou grafů atd. Ovšem mnohem zajímavější je kombinace Clojure s Jupyter Notebookem.
Pavel Tišnovský 14. 7. 2020
Doba čtení: 49 minut

Sdílet

11. Operace s maticemi

12. Incanter: prostředí pro statistické výpočty s grafickým výstupem založené na Clojure

13. Instalace prostředí Incanter

14. Ukázky základních možností při tvorbě grafů v Incanteru

15. Konverze diářů do zdrojových kódů jazyka Clojure

16. Historie příkazů zapisovaných do Jupyter Notebooku

17. Repositář s demonstračními příklady

18. Odkazy na články o Jupyter Notebooku

19. Odkazy na předchozí části seriálu o programovacím jazyku Clojure

20. Odkazy na Internetu

1. Interpret programovacího jazyka Clojure integrovaný do Jupyter Notebooku

Pro dnes velmi populární projekt Jupyter vznikla celá řada takzvaných kernelů, z nichž mnohé podporují další programovací jazyky (i když Python bude pravděpodobně stále nejpoužívanějším jazykem). Jedná se o různé typy jazyků, většinou o klasické interpretry, ovšem nalezneme zde i jazyky překládané (kompilované), jazyky určené pro běh nad virtuálními stroji (především JVM a CLR) i jazyky s vlastním virtuálním strojem. V dnešním článku nás budou zajímat jazyky provozované nad virtuálním strojem programovacího jazyka Java (se všemi z toho plynoucími výhodami a některými nevýhodami) a současně i jazyky z LISPovské větve, které mohou být v oblasti zpracování dat a interaktivního programování poměrně užitečné. V současnosti nad JVM běží hned několik jazyků z LISPovské větve. Především se jedná o Clojure a o sice méně známý, ale stále velmi zajímavý jazyk Kawa. V Jupyter Notebooku je podporován jazyk Clojure, a to dokonce několika kernely – viz též následující tabulku se zvýrazněnými řádky:

Kernel Jazyk
Dyalog Jupyter Kernel APL (Dyalog)
Coarray-Fortran Fortran 2008/2015
IJulia Julia
IHaskell ghc >= 7.6
IRuby ruby >= 2.3
tslab Typescript 3.7.2, JavaScript ESNext
IJavascript nodejs >= 0.10
ITypeScript Typescript >= 2.0
jpCoffeescript coffeescript >= 1.7
jp-LiveScript livescript >= 1.5
ICSharp C# 4.0+
IFSharp F#
lgo Go >= 1.8
iGalileo Galileo >= 0.1.3
gopherlab Go >= 1.6
Gophernotes Go >= 1.9
IScala Scala
IErlang Erlang
ITorch Torch 7 (LuaJIT)
IElixir Elixir >= 1.5
ierl Erlang >= 19, Elixir >= 1.4, LFE 1.2
OCaml-Jupyter OCaml >= 4.02
IForth Forth
peforth Forth
IPerl Perl 5
Perl6 Perl 6.c
IPerl6 Perl 6
Jupyter-Perl6 Perl 6.C
IPHP PHP >= 5.4
Jupyter-PHP PHP >= 7.0.0
IOctave Octave
IScilab Scilab
MATLAB Kernel Matlab
Bash bash
Z shell zsh >= 5.3
PowerShell PowerShell
CloJupyter Clojure >= 1.7
jupyter-kernel-jsr223 Clojure 1.8
Hy Kernel Hy
Calysto Hy Hy
jp-babel Babel
Lua Kernel Lua
IPurescript Purescript
IPyLua Lua
ILua Lua
Calysto Scheme Scheme
Calysto Processing Processing.js >= 2
idl_kernel IDL
Mochi Kernel Mochi
Lua (used in Splash) Lua
Calysto Bash bash
IBrainfuck Brainfuck
cling C++
xeus-cling C++
Prolog Prolog
SWI-Prolog SWI-Prolog
cl-jupyter Common Lisp
common-lisp-jupyter Common Lisp
IJython Jython 2.7
ROOT C++/python
Tcl Tcl 8.5
J J 805–807 (J901beta)
Jython Jython>=2.7.0
C C
Coconut Coconut
Pike Pike >= 7.8
jupyter-kotlin Kotlin 1.1-M04 EAP
mit-scheme-kernel MIT Scheme 9.2
elm-kernel elm
SciJava Jupyter Kernel Java + 9 scripting languages
BeakerX Groovy, Java, Scala, Clojure, Kotlin, SQL
IJava Java 9
Guile Guile 2.0.12
IRacket Racket >= 6.10
EvCxR Jupyter Kernel Rust >= 1.29.2
SSH Kernel Bash
Emu86 Kernel Intel Assembly Language
Poznámka: čtenář se samozřejmě může oprávněně ptát, proč by mělo být vůbec vhodné používat i v Jupyter Notebooku jazyk běžící nad JVM. Důvodů je více. Prvním z nich je, že virtuální stroj Javy dnes tvoří rozsáhlý ekosystém (vlastně již jen historicky svázaný s jazykem Java) a jakýkoli interpretovaný (!) jazyk, který umožní tento ekosystém používat a přistupovat k němu přímo z Jupyter Notebooku je velmi silnou technologií. A konkrétně programovací jazyk Clojure má velmi pěknou implementaci Kafka streamů, knihovny pro symbolické výpočty, a v oblasti grafů můžeme použít propracovanou knihovnu Oz (bude popsána v samostatném článku). Navíc JVM netrpí některými problémy typu GIL (důležité v současnosti, kdy je nutné výpočty poměrně masivně paralelizovat), ovšem na druhou stranu se například při práci s rozsáhlými poli můžeme dostat do problémů souvisejících s automatickým správcem paměti (Humongous Objects, což je však téma na samostatný článek).

2. Instalace využívající nástroj Conda

Nejprve si ukažme, jakým způsobem je možné nainstalovat Jupyter Notebook a kernel nazvaný Clojupyter, tedy jeden z kernelů, který umožňuje používat interpret programovacího jazyka Clojure přímo z Jupyter Notebooku. První varianta instalace je založena na použití nástroje Conda, což je – zjednodušeně řečeno – správce balíčků pro více programovacích jazyků, včetně Pythonu.

V prvním kroku se připraví virtuální prostředí pro Python:

$ conda create -n clojupyter_test python=3.6
Poznámka: konkrétní verzi Pythonu si upravte podle svých potřeb.

Dále následuje aktivace virtuálního prostředí Pythonu:

$ source activate clojupyter_test

Po aktivaci virtuálního prostředí následuje instalace Jupyter Notebooku a všech knihoven, na nichž tento dnes již velmi rozsáhlý a široce konfigurovatelný nástroj závisí:

$ conda install jupyter

Po úspěšném dokončení předchozího příkazu by měl být Jupyter Notebook připraven pro spuštění, samozřejmě s tím, že je v něm dostupný především kernel s interpretrem programovacího jazyka Python:

$ jupyter notebook

Obrázek 1: Úvodní stránka Jupyter Notebooku.

Nyní můžeme Jupyter Notebook zastavit, z konzole například pomocí klávesové zkratky Ctrl+D.

Druhá část instalace spočívá v naklonování repositáře se zdrojovými kódy kernelu Clojupyter:

$ git clone https://github.com/roryk/clojupyter

Po naklonování je nutné provést konfiguraci, překlad a instalaci tohoto kernelu, což vyžaduje běžný postup make+make install:

$ cd clojupyter
$ make
$ make install

Po novém spuštění Jupyter Notebooku by již měl být k dispozici i nový kernel:

$ jupyter notebook

Obrázek 2: V nabídce by se měla objevit nabídka nového kernelu.

3. Vytvoření kostry projektu umožňujícího spuštění Jupyter Notebooku

Existuje ovšem i alternativní možnost instalace, která předpokládá, že je na počítači již nainstalován Jupyter Notebook (viz například Instalace Jupyter Notebooku) a současně je k dispozici i programovací jazyk Clojure doplněný o nástroj Leiningen. Pokud jsou tyto podmínky splněny, lze instalaci provést tak, že se vytvoří kostra nového projektu, doplní se seznam pluginů, provede se instalace pluginů a spustí se Jupyter Notebook.

Vytvoření kostry projektu:

$ lein new app xyz

Výše uvedený příkaz by měl vytvořit nový podadresář nazvaný xyz, jehož interní struktura bude následující:

.
├── CHANGELOG.md
├── doc
│   └── intro.md
├── LICENSE
├── project.clj
├── README.md
├── resources
├── src
│   └── xyz
│       └── core.clj
└── test
    └── xyz
        └── core_test.clj

Ručně musíme provést jedinou úpravu, a to konkrétně v projektovém souboru project.clj, v němž je nutné přidat deklaraci pluginu nazvaného lein-jupyter. Provedená změna je naznačena tučně:

(defproject xyz "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.8.0"]]
  :main ^:skip-aot xyz.core
  :target-path "target/%s"
  :plugins [[lein-jupyter "0.1.16"]]
  :profiles {:uberjar {:aot :all}})

Dále se spustí příkaz zajišťující instalaci všech knihoven, na kterých projekt závisí:

$ lein deps

Podle aktuálního stavu systému se postahují desítky a v některých případech (čistá instalace Clojure) i stovky (pod)knihoven!

Poznámka: bližší informace o tom, jak funguje správa projektů v Clojure při použití nástroje Leiningen, již na Rootu byly prezentovány v článku Leiningen: nástroj pro správu projektů napsaných v Clojure.

4. Spuštění Jupyter Notebooku s novým kernelem

Nyní by již mělo být možné spustit (v adresáři s projektem!) Jupyter Notebook a otestovat, zda je k dispozici i nový kernel:

$ jupyter notebook
 
[I 16:29:19.360 NotebookApp] Writing notebook server cookie secret to /run/user/1000/jupyter/notebook_cookie_secret
[W 16:29:19.833 NotebookApp] WARNING: The notebook server is listening on all IP addresses and not using encryption. This is not recommended.
[I 16:29:19.844 NotebookApp] Serving notebooks from local directory: /home/ptisnovs/temp/xyz
[I 16:29:19.844 NotebookApp] 0 active kernels
[I 16:29:19.844 NotebookApp] The Jupyter Notebook is running at:
[I 16:29:19.844 NotebookApp] http://[all ip addresses on your system]:8888/
[I 16:29:19.844 NotebookApp] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation).
[I 16:29:20.147 NotebookApp] 302 GET /tree (::1) 1.26ms
[I 16:29:22.596 NotebookApp] 302 POST /login?next=%2Ftree (::1) 2.21ms

Může se však stát, že se v terminálu objeví chybová zpráva o tom, že není nastavena proměnná prostředí LEIN_WORKING_DIRECTORY:

Traceback (most recent call last):
  File "/home/ptisnovs/.local/share/jupyter/kernels/lein-clojure/leinclojure.py", line 12, in <module>
    raise RuntimeError("The environment variable 'LEIN_WORKING_DIRECTORY'"
RuntimeError: The environment variable 'LEIN_WORKING_DIRECTORY' for this kernel to run

V tomto případě Jupyter Notebook ukončete (postačuje stisknout klávesovou zkratku Ctrl+C v terminálu) a proměnnou nastavte na aktuální adresář:

$ export LEIN_WORKING_DIRECTORY=.

Nové spuštění by již mělo proběhnout bez chyby:

$ jupyter notebook
 
[W 16:32:28.633 NotebookApp] WARNING: The notebook server is listening on all IP addresses and not using encryption. This is not recommended.
[I 16:32:28.643 NotebookApp] Serving notebooks from local directory: /home/ptisnovs/temp/xyz
[I 16:32:28.643 NotebookApp] 0 active kernels
[I 16:32:28.643 NotebookApp] The Jupyter Notebook is running at:
[I 16:32:28.643 NotebookApp] http://[all ip addresses on your system]:8888/
[I 16:32:28.643 NotebookApp] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation).
[I 16:32:31.865 NotebookApp] Creating new notebook in
[I 16:32:32.514 NotebookApp] Kernel started: f6363137-8b71-4e78-83d8-d65a425e58a1
[I 16:34:32.495 NotebookApp] Saving file at /Untitled1.ipynb

Obrázek 3: Prázdný diář s připraveným interpretrem programovacího jazyka Clojure.

5. Základní použití Clojure v Jupyter Notebooku, načtení standardních modulů

Diář s kernelem Clojupyer se chová podobně, jako například diář s kernelem programovacího jazyka Python. Editace buněk, přesuny buněk, vyhodnocení obsahu buněk atd. lze provádět buď s využitím myši (velmi rychle přijdete na to, že jsou to pomalé operace) nebo pomocí klávesových zkratek, které jsou aktivní v příkazovém režimu (command mode). Do tohoto režimu se dostanete stiskem klávesy Esc. Mezi nejdůležitější klávesové zkratky, jen je možné využít prakticky ve všech typech diářů, patří:

# Klávesová zkratka Popis provedené operace
1 Esc+Enter editace buňky (důležité například u buněk s Markdownem)
2 Esc+A vytvoření nové buňky nad buňkou aktivní (above)
3 Esc+B vytvoření nové buňky pod buňkou aktivní (below)
     
4 Esc+X cut
5 Esc+C copy
6 Esc+V paste
7 Esc+D,D vymazání buňky (D se stiskne dvakrát)
     
8 Esc+S uložení celého diáře
9 Esc+Y přepnutí typu buňky (jejího obsahu) na programový kód
10 Esc+M přepnutí typu buňky (jejího obsahu) na Markdown
     
11 Esc+H zobrazení nápovědy k příkazovému režimu

Obrázek 4: Nápověda k příkazovému režimu (command mode).

Obrázek 5: Nápověda k editačnímu režimu (edit mode).

Konkrétně v Clojupyteru je navíc implementováno automatické doplňování názvů funkcí, maker či symbolů pomocí klávesy Tab a taktéž zvýraznění párových závorek, což je pro LISPovský programovací jazyk neocenitelná výhoda.

Jedním z rozdílů mezi Clojupyterem a standardní interaktivní smyčkou REPL je fakt, že se do Clojupyteru implicitně nenačítají všechny standardní moduly. To se týká i velmi užitečného makra určeného pro zobrazení dokumentace:

Obrázek 6: Po spuštění Clojupyteru není k dispozici makro doc.

Řešení je jednoduché – na začátku diáře je možné všechny potřebné importy provést. Týká se to především jmenného prostoru clojure.repl, který se načte příkazem:

(use 'clojure.repl)

Obrázek 7: Načtení modulu clojure.repl. Po provedení tohoto příkazu je již možné bez problémů používat makro doc i další funkce a makra z tohoto jmenného prostoru.

6. Automatické zobrazení rastrových obrázků typu java.awt.image.BufferedImage

Jednou z velmi dobrých a prakticky použitelných vlastností kernelu Clojupyer je jeho schopnost automaticky zobrazit rastrové obrázky reprezentované objektem typu java.awt.image.BufferedImage. Co to v praxi znamená? Pokud nějaká forma (neboli zjednodušeně řečeno výraz) zapsaná do buňky vrátí objekt tohoto typu, je rastrový obrázek rozpoznán a zobrazen přímo na ploše diáře. Tímto způsobem lze vytvářet všechny typy diagramů, grafů, výsledků různých simulací atd. Ostatně si můžeme vyzkoušet jednoduchý příklad:

(import java.awt.image.BufferedImage)
 
(defn create
  "Create new bitmap."
  [width height]
  (new BufferedImage width height BufferedImage/TYPE_INT_RGB))
 
(create 256 256)

Tento příklad (zapsaný do diáře) by měl zobrazit prázdnou (černou) bitmapu o rozměrech 256×256 pixelů:

Obrázek 8: Rastrový obrázek o rozměrech 256×256 zobrazený v ploše diáře.

Nic nám samozřejmě nebrání do obrázku vykreslit nějaké tvary, popř. postupně vybarvit jeho pixely:

(import java.awt.image.BufferedImage)
 
(defn create
  "Create new bitmap."
  [width height]
  (new BufferedImage width height BufferedImage/TYPE_INT_RGB))
 
(defn ->rgb
  [r g b]
  (bit-or b
          (bit-shift-left g 8)
          (bit-shift-left r 16)))
 
(defn putpixel
  "Change pixel color."
  ( [bitmap x y rgb]
    (.setRGB bitmap x y rgb))
  ( [bitmap x y r g b]
    (.setRGB bitmap x y (->rgb r g b))))
 
(create 256 256)
 
(let [bitmap (create 256 256)]
    (doseq [y (range 256)]
        (doseq [x (range 256)]
            (putpixel bitmap x y x 255 y)))
    bitmap)

Výsledek by měl vypadat následovně:

Obrázek 9: Rastrový obrázek o rozměrech 256×256 zobrazený v ploše diáře. Pixely byly individuálně vybarveny.

Poznámka: výraz let v poslední buňce pracuje tak, že vrací hodnotu posledního výrazu uvnitř svého těla, což je zde konkrétně výraz bitmap.

7. Typické knihovny a frameworky, které lze využít v kombinaci Jupyter Notebooku s jazykem Clojure

Již v úvodní kapitole jsme si řekli, že jedním z důvodů pro použití kernelu Clojupyter může být fakt, že z programovacího jazyka Clojure je možné přistupovat k obrovskému množství knihoven a frameworků vytvořených pro ekosystém okolo JVM. Kromě toho ovšem vznikly i vhodné knihovny určené přímo pro jazyk Clojure. V kontextu typického použití Jupyter Notebooku jsou zajímavé především dvě knihovny – clojure.core.matrix (či jen core.matrix) a taktéž projekt Incanter, resp. přesněji řečeno knihovny, které jsou v rámci tohoto projektu dostupné. Zatímco core.matrix svým záběrem zhruba odpovídá knihovně Numpy pro Python, je Incanter mnohem rozsáhlejší, protože kromě statistických výpočtů nabízí uživatelům i možnost zobrazení různých typů grafů, tj. může se jednat o alternativu k projektu Matplotlib, s nímž jsme se již v seriálu o Jupyter Notebooku alespoň ve stručnosti seznámili.

matplotlib

Obrázek 10: Graf zobrazený Matplotlibem.

8. Knihovna clojure.core.matrix

V této kapitole si připomeneme existenci knihovny pojmenované core.matrix či plným označením clojure.core.matrix, která vývojářům pracujícím s programovacím jazykem Clojure nabízí prakticky všechny potřebné operace, které jsou vyžadovány při práci s vektory a maticemi (včetně například výpočtu inverzní matice). Navíc je tato knihovna zajímavá tím, že předepisuje rozhraní pro všechny operace, ovšem konkrétní implementaci je možné si vybrat. To například znamená, že pokud je řešen nějaký problém, v němž se ve velké míře používají takzvané řídké matice (pěkným příkladem z praxe může být Google matrix), lze – beze změny zdrojového kódu uživatelského programu – vybrat takovou reprezentaci matic a takové algoritmy, které jsou optimalizovány právě pro práci s řídkými maticemi a nikoli s maticemi uloženými ve formě „hutného“ dvourozměrného pole. Díky tomuto přístupu a taktéž díky dalším vlastnostem programovacího jazyka Clojure (existence tzv. threading makra a funkcí vyššího řádu) se práce s maticemi do značné míry začíná podobat práci v programovacím jazyku APL, až na ten rozdíl, že algoritmy zapisované v Clojure jsou pro většinu vývojářů přece jen více čitelnější :-).

Nejprve si knihovnu core.matrix nainstalujeme. Je to snadné – postačuje vytvořit nový projekt:

$ lein new app enter-the-matrix

Následně upravit projektový soubor – přidat do něj novou závislost:

(defproject matrix1 "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.8.0"]
                 [net.mikera/core.matrix "0.62.0"]]
  :main ^:skip-aot matrix1.core
  :target-path "target/%s"
  :profiles {:uberjar {:aot :all}})

A spustit příkaz pro zjištění a stažení všech závislostí:

$ lein deps

V dnešním článku však použijeme poněkud jiný projektový soubor, který navíc obsahuje deklaraci pluginu umožňujícího spuštění Jupyter Notebooku s novým kernelem:

(defproject xyz "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.8.0"]
                 [net.mikera/core.matrix "0.62.0"]]
  :main ^:skip-aot xyz.core
  :target-path "target/%s"
  :plugins [[lein-jupyter "0.1.16"]]
  :profiles {:uberjar {:aot :all}})

Pokud se Jupyter Notebook spustí z adresáře s tímto projektovým souborem, bude možné core.matrix přímo použít v Jupyter Notebooku.

9. Představení možností knihovny clojure.core.matrix

Některé základní možnosti knihovny clojure.core.matrix jsou prezentovány na diáři dostupném na adrese https://github.com/tisnik/jupyter-notebook-examples/tree/master/Clojupyter%20-%20introduction%20to%20clo­jure.core.matrix.ipynb. Můžeme v něm vidět dvojici příkazů use, které načtou všechny funkce a všechna makra ze jmenných prostorů clojure.core.matrix a clojure.core.matrix.operators. Následně se vytvoří čtvercová matice nazvaná M a dvouprvkový vektor nazvaný v, s nimiž je provedeno několik operací. Funkce pm slouží pro výpis obsahu matice na standardní výstup v „pěkném“ tvaru (pretty print). Lze samozřejmě použít i funkci print či println:

(use 'clojure.core.matrix)
(use 'clojure.core.matrix.operators)
 
(let [M (matrix [[1 2] [3 4]])
      v (matrix [1 2])]
      (pm M)
      (pm v)
      (pm (* v 2))
      (pm (mul M v))
      (pm (* M M))
      (pm (inner-product M v)))
 
; vektor
(pm (matrix [1 2 3]))
 
; vektor
(pm (matrix '(1 2 3)))
 
; matice
(pm (matrix [[1 2] [3 4]]))
 
; matice
(pm (matrix (range 1 10)))
 
; matice
(pm (matrix [[1 2 3] [4 5 6] [7 8 9]]))
 
(zero-matrix 10 1)
(pm *1)
 
(zero-matrix 1 10)
(pm *1)
 
(zero-matrix 2 3)
(pm *1)
 
(zero-matrix 4 4)
(pm *1)
 
(identity-matrix 4 4)
(pm *1)
 
; vektor udává pozice jedniček na jednotlivých řádcích matice
; rozměry matice jsou získány na základě velikosti tohoto vektoru
(permutation-matrix [1 4 2 3 0])
 
(pm *1)
Poznámka: zajímavé jsou formy (pm *1). Ty říkají, že se má vytisknout obsah poslední známé výsledné hodnoty, kterou si prostředí Clojupyter (popř. interaktivní smyčka REPL) pamatuje. Použití *1 tak představuje velmi významnou součást interaktivního prostředí programovacího jazyka Clojure.

Obrázek 10: Diář se základními operacemi pro práci s maticemi zobrazený v Jupyter Notebooku.

10. Operace s vektory

V knihovně core.matrix je deklarováno velké množství funkcí, které dokážou pracovat s celými vektory, popř. s jednotlivými prvky, z nichž se vektory skládají. Podívejme se nyní na úryvek z demonstračního příkladu:

(defn immutable-vector-functions
    "Test chování vybraných funkcí, které lze nalézt ve jmenných prostorech
     clojure.core.matrix a clojure.core.matrix.operators."
    [vector1 vector2 vector3]
    (println "*** Test funkcí aplikovaných na vektory ***")
    (println "Vstupní data (vektory):")
    (print-vector vector1)
    (print-vector vector2)
    (print-vector vector3)
    (println "Vyhodnocení:")
    (print-vector (emin vector1))
    (print-vector (emin vector2))
    (print-vector (emin vector3))
    (print-vector (emax vector1))
    (print-vector (emax vector2))
    (print-vector (emax vector3))
    (print-vector (normalise vector1))
    (print-vector (normalise vector2))
    (print-vector (normalise vector3))
    (print-vector (length vector1))
    (print-vector (length vector2))
    (print-vector (length vector3))
    (print-vector (length-squared vector1))
    (print-vector (length-squared vector2))
    (print-vector (length-squared vector3))
    (print-vector (distance vector1 vector2))
    (print-vector (distance vector1 vector3))
    (print-vector (distance vector2 vector3))
    (print-vector (join vector1 vector2))
    (print-vector (join vector1 vector2 vector3))
    (println))

Nejprve se podívejme na výsledky vypsané po spuštění této funkce:

*** Test funkcí aplikovaných na vektory ***
Vstupní data (vektory):
    vector1                          =  [1.000 2.000 3.000]
    vector2                          =  [2.000 3.000 4.000]
    vector3                          =  [1.000 1.000]
Vyhodnocení:
    (emin vector1)                   =  1.000
    (emin vector2)                   =  2.000
    (emin vector3)                   =  1.000
    (emax vector1)                   =  3.000
    (emax vector2)                   =  4.000
    (emax vector3)                   =  1.000
    (normalise vector1)              =  [0.267 0.535 0.802]
    (normalise vector2)              =  [0.371 0.557 0.743]
    (normalise vector3)              =  [0.707 0.707]
    (length vector1)                 =  3.742
    (length vector2)                 =  5.385
    (length vector3)                 =  1.414
    (length-squared vector1)         =  14.000
    (length-squared vector2)         =  29.000
    (length-squared vector3)         =  2.000
    (distance vector1 vector2)       =  1.732
    (distance vector1 vector3)       =  1.000
    (distance vector2 vector3)       =  2.236
    (join vector1 vector2)           =  [1.000 2.000 3.000 2.000 3.000 4.000]
    (join vector1 vector2 vector3)   =  [1.000 2.000 3.000 2.000 3.000 4.000 1.000 1.000]

Význam jednotlivých funkcí použitých v předchozím příkladu je vysvětlen v tabulce:

# Funkce Výsledek Popis
1 emin skalár nalezne nejmenší prvek vektoru
2 emax skalár nalezne největší prvek vektoru
3 normalize vektor normalizace vektoru (na délku rovnou jedné)
4 length skalár výpočet délky vektoru
5 length-squared skalár výpočet druhé mocniny délky vektoru
6 distance skalár dva vektory jsou chápány jako 2D/3D body, vypočítá se jejich vzdálenost
7 join vektor spojení dvou čí většího množství vektorů
Poznámka: žádná z těchto funkcí nemodifikuje původní vektor, vždy je vytvořena nová datová struktura.

V předchozím odstavci jsme se mj. zmínili i o funkci/operátoru *, který slouží k vynásobení těch prvků vektorů, které mají shodný index, tj. první prvek vektoru číslo 1 je vynásoben s prvním prvkem vektoru číslo 2 atd. Kromě toho se však v mnoha disciplínách (mj. i v počítačové grafice) pracuje se skalárním a vektorovým součinem. I tyto operace jsou samozřejmě v knihovně core.matrix podporovány:

# Funkce Výsledek Popis
1 dot skalár skalární součin dvou vektorů
2 inner-product skalár zobecnění skalárního součinu (pro matice má význam násobení matic)
3 cross vektor vektorový součin dvou vektorů

Opět se podívejme na způsob použití těchto funkcí:

(defn immutable-vector-products
    "Skalární a vektorový součin dvou trojsložkových vektorů."
    [vector1 vector2]
    (println "*** Skalární a vektorový součin dvou trojsložkových vektorů ***")
    (println "Vstupní data (vektory):")
    (print-vector vector1)
    (print-vector vector2)
    (println "Vyhodnocení:")
    (print-vector (dot vector1 vector1))
    (print-vector (dot vector1 vector2))
    (print-vector (dot vector2 vector1))
    (print-vector (dot vector2 vector2))
    (print-vector (inner-product vector1 vector1))
    (print-vector (inner-product vector1 vector2))
    (print-vector (inner-product vector2 vector1))
    (print-vector (inner-product vector2 vector2))
    (print-vector (cross vector1 vector1))
    (print-vector (cross vector1 vector2))
    (print-vector (cross vector2 vector1))
    (print-vector (cross vector2 vector2))
    (println))
 
(defn immutable-vector-tests
    "Spustí všechny testovací funkce s neměnitelnými vektory."
    []
    (let [v0 (zero-vector 3)
          v1 (array [1 2 3])
          v2 (array [2 3 4])
          v3 (array [1 1])]
          (immutable-vector-products v1 v2)
          (immutable-vector-products v0 v1)))

Výsledek zavolání testovací funkce:

*** Skalární a vektorový součin dvou trojsložkových vektorů ***
Vstupní data (vektory):
    vector1                          =  [1.000 2.000 3.000]
    vector2                          =  [2.000 3.000 4.000]
Vyhodnocení:
    (dot vector1 vector1)            =  14.000
    (dot vector1 vector2)            =  20.000
    (dot vector2 vector1)            =  20.000
    (dot vector2 vector2)            =  29.000
    (inner-product vector1 vector1)  =  14.000
    (inner-product vector1 vector2)  =  20.000
    (inner-product vector2 vector1)  =  20.000
    (inner-product vector2 vector2)  =  29.000
    (cross vector1 vector1)          =  [0.000 0.000 0.000]
    (cross vector1 vector2)          =  [-1.000 2.000 -1.000]
    (cross vector2 vector1)          =  [1.000 -2.000 1.000]
    (cross vector2 vector2)          =  [0.000 0.000 0.000]
 
*** Skalární a vektorový součin dvou trojsložkových vektorů ***
Vstupní data (vektory):
    vector1                          =  [0.000 0.000 0.000]
    vector2                          =  [1.000 2.000 3.000]
Vyhodnocení:
    (dot vector1 vector1)            =  0.000
    (dot vector1 vector2)            =  0.000
    (dot vector2 vector1)            =  0.000
    (dot vector2 vector2)            =  14.000
    (inner-product vector1 vector1)  =  0.000
    (inner-product vector1 vector2)  =  0.000
    (inner-product vector2 vector1)  =  0.000
    (inner-product vector2 vector2)  =  14.000
    (cross vector1 vector1)          =  [0.000 0.000 0.000]
    (cross vector1 vector2)          =  [0.000 0.000 0.000]
    (cross vector2 vector1)          =  [0.000 0.000 0.000]
    (cross vector2 vector2)          =  [0.000 0.000 0.000]

Obrázek 11: Diář s operacemi s vektory.

11. Operace s maticemi

Nejdůležitější datovou strukturou, s níž se v knihovně core.matrix pracuje, jsou samozřejmě matice, především pak dvourozměrné matice. Tyto matice mohou mít libovolné rozměry a při jejich vytváření je možné si zvolit jak způsob jejich uložení v operační paměti počítače, tak i to, zda se bude jednat o matice měnitelné (mutable) či neměnitelné (immutable). V první řadě se jedná o funkce sloužící pro vytvoření matic různých typů, viz též následující tabulku:

# Funkce Výsledek Popis
1 matrix matice vytvoření matice a inicializace jejích prvků
2 zero-matrix matice vytvoření matice s nulovými prvky
3 identity-matrix matice vytvoření jednotkové matice
4 permutation-matrix matice vytvoření matice, v níž jsou jedničky umístěny podle specifikovaného vektoru

Další velmi důležitou funkcí je funkce reshape, která dokáže změnit velikost matice a vhodným způsobem přeorganizovat prvky v původní matici.

Podívejme se nyní na některé další funkce, které lze použít pro práci s maticemi. Nejprve se budeme zabývat již zmíněnými funkcemi/operátory, které mají u matic v některých případech odlišný význam, než tomu bylo u vektorů:

# Funkce Výsledek Popis
1 + matice součet matic (může se provést broadcast)
2 matice rozdíl matic (může se provést broadcast)
3 * matice maticový součin (nikoli prvek po prvku!)
4 / matice maticový součin s inverzní maticí

Chování těchto funkcí/operátorů si samozřejmě vyzkoušíme v praxi:

(defn immutable-matrix-tests
    "Spustí všechny testovací funkce s neměnitelnými maticemi"
    []
    (let [m0 (zero-matrix 3 3)
          m1 (identity-matrix 3 3)
          m2 (array [[1 2 3] [4 5 6] [7 8 9]])
          m3 (array [[0 1] [2 3]])]
          (immutable-matrix-operators m0 m1)
          (immutable-matrix-operators m1 m2)
          ;(immutable-matrix-special-operators m1 m2)  ; bude použito níže
          ;(immutable-matrix-functions m1 m2 m3)))     ; bude použito níže
(defn immutable-matrix-operators
    "Test chování operátorů předefinovaných ve jmenném prostoru
     clojure.core.matrix.operators."
    [matrix1 matrix2]
    (println "*** Test základních operátorů aplikovaných na matice ***")
    (println "Vstupní data (matice):")
    (print-matrix matrix1)
    (print-matrix matrix2)
    (println "Vyhodnocení:")
    ; operace provedené mezi maticí a skalární hodnotou
    (print-matrix (+ matrix1 10))
    (print-matrix (- matrix1 10))
    (print-matrix (* matrix1 10))
    (print-matrix (/ matrix1 10))
    ; operace provedené mezi skalární hodnotou a maticí
    (print-matrix (+ 10 matrix1))
    (print-matrix (- 10 matrix1))
    (print-matrix (* 10 matrix1))
    (try
        (print-matrix (/ 10 matrix1))
        (catch java.lang.ArithmeticException e
            (println "Exception: " (.toString e))))
    ; operace provedené mezi dvěma maticemi
    (print-matrix (+ matrix1 matrix2))
    (print-matrix (- matrix1 matrix2))
    (print-matrix (* matrix1 matrix2))
    (try
        (print-matrix (/ matrix1 matrix2))
        (catch java.lang.ArithmeticException e
            (println "Exception: " (.toString e))))
    (println))

Výsledky vypadají následovně (povšimněte si, že může dojít k vyhození výjimky při dělení nulou):

*** Test základních operátorů aplikovaných na matice ***
Vstupní data (matice):
    matrix1 =
[[0.000 0.000 0.000]
 [0.000 0.000 0.000]
 [0.000 0.000 0.000]]
 
    matrix2 =
[[1.000 0.000 0.000]
 [0.000 1.000 0.000]
 [0.000 0.000 1.000]]
 
Vyhodnocení:
    (+ matrix1 10) =
[[10.000 10.000 10.000]
 [10.000 10.000 10.000]
 [10.000 10.000 10.000]]
 
    (- matrix1 10) =
[[-10.000 -10.000 -10.000]
 [-10.000 -10.000 -10.000]
 [-10.000 -10.000 -10.000]]
 
    (* matrix1 10) =
[[0.000 0.000 0.000]
 [0.000 0.000 0.000]
 [0.000 0.000 0.000]]
 
    (/ matrix1 10) =
[[0.000 0.000 0.000]
 [0.000 0.000 0.000]
 [0.000 0.000 0.000]]
 
    (+ 10 matrix1) =
[[10.000 10.000 10.000]
 [10.000 10.000 10.000]
 [10.000 10.000 10.000]]
 
    (- 10 matrix1) =
[[10.000 10.000 10.000]
 [10.000 10.000 10.000]
 [10.000 10.000 10.000]]
 
    (* 10 matrix1) =
[[0.000 0.000 0.000]
 [0.000 0.000 0.000]
 [0.000 0.000 0.000]]
 
    (/ 10 matrix1) =
Exception:  java.lang.ArithmeticException: Divide by zero
 
    (+ matrix1 matrix2) =
[[1.000 0.000 0.000]
 [0.000 1.000 0.000]
 [0.000 0.000 1.000]]
 
    (- matrix1 matrix2) =
[[-1.000  0.000  0.000]
 [ 0.000 -1.000  0.000]
 [ 0.000  0.000 -1.000]]
 
    (* matrix1 matrix2) =
[[0.000 0.000 0.000]
 [0.000 0.000 0.000]
 [0.000 0.000 0.000]]
 
    (/ matrix1 matrix2) =
Exception:  java.lang.ArithmeticException: Divide by zero
 
*** Test základních operátorů aplikovaných na matice ***
Vstupní data (matice):
    matrix1 =
[[1.000 0.000 0.000]
 [0.000 1.000 0.000]
 [0.000 0.000 1.000]]
 
    matrix2 =
[[1.000 2.000 3.000]
 [4.000 5.000 6.000]
 [7.000 8.000 9.000]]
 
Vyhodnocení:
    (+ matrix1 10) =
[[11.000 10.000 10.000]
 [10.000 11.000 10.000]
 [10.000 10.000 11.000]]
 
    (- matrix1 10) =
[[ -9.000 -10.000 -10.000]
 [-10.000  -9.000 -10.000]
 [-10.000 -10.000  -9.000]]
 
    (* matrix1 10) =
[[10.000  0.000  0.000]
 [ 0.000 10.000  0.000]
 [ 0.000  0.000 10.000]]
 
    (/ matrix1 10) =
[[0.100 0.000 0.000]
 [0.000 0.100 0.000]
 [0.000 0.000 0.100]]
 
    (+ 10 matrix1) =
[[11.000 10.000 10.000]
 [10.000 11.000 10.000]
 [10.000 10.000 11.000]]
 
    (- 10 matrix1) =
[[ 9.000 10.000 10.000]
 [10.000  9.000 10.000]
 [10.000 10.000  9.000]]
 
    (* 10 matrix1) =
[[10.000  0.000  0.000]
 [ 0.000 10.000  0.000]
 [ 0.000  0.000 10.000]]
 
    (/ 10 matrix1) =
Exception:  java.lang.ArithmeticException: Divide by zero
 
    (+ matrix1 matrix2) =
[[2.000 2.000  3.000]
 [4.000 6.000  6.000]
 [7.000 8.000 10.000]]
 
    (- matrix1 matrix2) =
[[ 0.000 -2.000 -3.000]
 [-4.000 -4.000 -6.000]
 [-7.000 -8.000 -8.000]]
 
    (* matrix1 matrix2) =
[[1.000 0.000 0.000]
 [0.000 5.000 0.000]
 [0.000 0.000 9.000]]
 
    (/ matrix1 matrix2) =
[[1.000 0.000 0.000]
 [0.000 0.200 0.000]
 [0.000 0.000 0.111]]

Zajímavé je i chování operátorů ** (aplikován prvek po prvku s případným broadcastem) a == (taktéž aplikován prvek po prvku):

(defn immutable-matrix-special-operators
    "Test chování speciálních operátorů předefinovaných
     ve jmenném prostoru clojure.core.matrix.operators."
    [matrix1 matrix2]
    (println "*** Test speciálních operátorů aplikovaných na matice ***")
    (println "Vstupní data (matice):")
    (print-matrix matrix1)
    (print-matrix matrix2)
    (println "Vyhodnocení:")
    ; operace provedené mezi maticí a skalární hodnotou
    (print-matrix (** matrix1 10))
    ; operace provedené mezi skalární hodnotou a maticí
    (print-matrix (** 10 matrix1))
    ; operace provedené mezi dvěma maticemi
    (print-matrix (** matrix1 matrix2))
    ; operátor ekvivalence aplikovaný na dvojici matic
    (print-vector (== matrix1 matrix2))
    (print-vector (== matrix1 matrix1))
    (print-vector (== matrix1 (identity-matrix 3 3)))
    (print-vector (== matrix2 (array [[1 2 3] [4 5 6] [7 8 9]])))
    (println))

Opět se podívejme na výsledky:

*** Test speciálních operátorů aplikovaných na matice ***
Vstupní data (matice):
    matrix1 =
[[1.000 0.000 0.000]
 [0.000 1.000 0.000]
 [0.000 0.000 1.000]]
 
    matrix2 =
[[1.000 2.000 3.000]
 [4.000 5.000 6.000]
 [7.000 8.000 9.000]]
 
Vyhodnocení:
    (** matrix1 10) =
[[1.000 0.000 0.000]
 [0.000 1.000 0.000]
 [0.000 0.000 1.000]]
 
    (** 10 matrix1) =
[[10.000  1.000  1.000]
 [ 1.000 10.000  1.000]
 [ 1.000  1.000 10.000]]
 
    (** matrix1 matrix2) =
[[1.000 0.000 0.000]
 [0.000 1.000 0.000]
 [0.000 0.000 1.000]]
 
    (== matrix1 matrix2)             =  false
    (== matrix1 matrix1)             =  true
    (== matrix1 (identity-matrix 3 3)) =  true
    (== matrix2 (array [[1 2 3] [4 5 6] [7 8 9]])) =  true

Podívejme se ještě na některé významné funkce, které je možné aplikovat na matice. Některé z těchto funkcí vrací skalární hodnotu, jiné naopak vektor či (jinou) matici. Některé z těchto funkcí již známe z kapitol o vektorech, další funkce jsou zcela nové:

# Funkce Výsledek Popis
1 emin skalár nalezne nejmenší prvek matice
2 emax skalár nalezne největší prvek matice
3 esum skalár sečte prvky v matici
4 ecount skalár vrátí celkový počet prvků matice
5 zero-count skalár vrátí počet nulových prvků matice
6 inverse matice výpočet inverzní matice
7 non-zero-indices matice vrací indexy nenulových prvků pro všechny řezy maticí
8 outer-product matice (vyšší počet dimenzí) vnější součin matice/matic či vektorů
9 join matice spojení dvou či více matic (zvětší se počet řádků)

Nezbývá než si chování výše popsaných funkcí vyzkoušet:

(defn immutable-matrix-functions
    "Test chování vybraných funkcí, které lze nalézt ve jmenných prostorech
     clojure.core.matrix a clojure.core.matrix.operators."
    [matrix1 matrix2 matrix3]
    (println "*** Test funkcí aplikovaných na matice ***")
    (println "Vstupní data (matice):")
    (print-matrix matrix1)
    (print-matrix matrix2)
    (print-matrix matrix3)
    (println "Vyhodnocení:")
    (print-vector (emin matrix1))
    (print-vector (emin matrix2))
    (print-vector (emin matrix3))
    (print-vector (emax matrix1))
    (print-vector (emax matrix2))
    (print-vector (emax matrix3))
    (print-vector (esum matrix1))
    (print-vector (esum matrix2))
    (print-vector (esum matrix3))
    (print-vector (ecount matrix1))
    (print-vector (ecount matrix2))
    (print-vector (ecount matrix3))
    (print-vector (zero-count matrix1))
    (print-vector (zero-count matrix2))
    (print-vector (zero-count matrix3))
    (print-matrix (inverse matrix1))
    (print-matrix (inverse matrix2))
    (print-matrix (inverse matrix3))
    (print-matrix (non-zero-indices matrix1))
    (print-matrix (non-zero-indices matrix2))
    (print-matrix (non-zero-indices matrix3))
    (print-matrix (outer-product matrix1))
    (print-matrix (outer-product matrix2))
    (print-matrix (outer-product matrix3))
    (print-matrix (join matrix1 matrix2))
    (println))

Po zavolání této funkce by se na standardní výstup měly vypsat následující řádky:

*** Test funkcí aplikovaných na matice ***
Vstupní data (matice):
    matrix1 =
[[1.000 0.000 0.000]
 [0.000 1.000 0.000]
 [0.000 0.000 1.000]]
 
    matrix2 =
[[1.000 2.000 3.000]
 [4.000 5.000 6.000]
 [7.000 8.000 9.000]]
 
    matrix3 =
[[0.000 1.000]
 [2.000 3.000]]
 
Vyhodnocení:
    (emin matrix1)                   =  0.000
    (emin matrix2)                   =  1.000
    (emin matrix3)                   =  0.000
    (emax matrix1)                   =  1.000
    (emax matrix2)                   =  9.000
    (emax matrix3)                   =  3.000
    (esum matrix1)                   =  3.000
    (esum matrix2)                   =  45.000
    (esum matrix3)                   =  6.000
    (ecount matrix1)                 =  9.000
    (ecount matrix2)                 =  9.000
    (ecount matrix3)                 =  4.000
    (zero-count matrix1)             =  6.000
    (zero-count matrix2)             =  0.000
    (zero-count matrix3)             =  1.000
 
    (inverse matrix1) =
[[1.000 0.000 0.000]
 [0.000 1.000 0.000]
 [0.000 0.000 1.000]]
 
    (inverse matrix2) =
[[-4503599627370498.000   9007199254740992.000 -4503599627370496.000]
 [ 9007199254740996.000 -18014398509481984.000  9007199254740991.000]
 [-4503599627370498.000   9007199254740992.000 -4503599627370495.500]]
 
    (inverse matrix3) =
[[-1.500 0.500]
 [ 1.000 0.000]]
 
    (non-zero-indices matrix1) =
[[0.000]
 [1.000]
 [2.000]]
 
    (non-zero-indices matrix2) =
[[0.000 1.000 2.000]
 [0.000 1.000 2.000]
 [0.000 1.000 2.000]]
 
    (non-zero-indices matrix3) =
[[1.000]
 [0.000]]
 
    (outer-product matrix1) =
[[1.000 0.000 0.000]
 [0.000 1.000 0.000]
 [0.000 0.000 1.000]]
 
    (outer-product matrix2) =
[[1.000 2.000 3.000]
 [4.000 5.000 6.000]
 [7.000 8.000 9.000]]
 
    (outer-product matrix3) =
[[0.000 1.000]
 [2.000 3.000]]
 
    (join matrix1 matrix2) =
[[1.000 0.000 0.000]
 [0.000 1.000 0.000]
 [0.000 0.000 1.000]
 [1.000 2.000 3.000]
 [4.000 5.000 6.000]
 [7.000 8.000 9.000]]

Vnější součin dvou matic již dá složitější výsledek (jedná se vlastně o výsledek součinu každého prvku s každým prvkem a uspořádání do matice se čtyřmi rozměry):

(pm (outer-product m2 m2))
 
[[[[ 1.000  2.000  3.000]
   [ 4.000  5.000  6.000]
   [ 7.000  8.000  9.000]]
  [[ 2.000  4.000  6.000]
   [ 8.000 10.000 12.000]
   [14.000 16.000 18.000]]
  [[ 3.000  6.000  9.000]
   [12.000 15.000 18.000]
   [21.000 24.000 27.000]]]
 [[[ 4.000  8.000 12.000]
   [16.000 20.000 24.000]
   [28.000 32.000 36.000]]
  [[ 5.000 10.000 15.000]
   [20.000 25.000 30.000]
   [35.000 40.000 45.000]]
  [[ 6.000 12.000 18.000]
   [24.000 30.000 36.000]
   [42.000 48.000 54.000]]]
 [[[ 7.000 14.000 21.000]
   [28.000 35.000 42.000]
   [49.000 56.000 63.000]]
  [[ 8.000 16.000 24.000]
   [32.000 40.000 48.000]
   [56.000 64.000 72.000]]
  [[ 9.000 18.000 27.000]
   [36.000 45.000 54.000]
   [63.000 72.000 81.000]]]]

Jednodušší je vysvětlení chování této funkce na dvojici vektorů – jednorozměrných matic:

(def v1 (array [1 2 3]))
 
(pm (outer-product v1 v1))
 
[[1.000 2.000 3.000]
 [2.000 4.000 6.000]
 [3.000 6.000 9.000]]

Příklad z praxe – tabulka malé násobilky:

(def v1 (array (range 1 11)))
 
(pm (outer-product v1 v1))
 
[[ 1.000  2.000  3.000  4.000  5.000  6.000  7.000  8.000  9.000  10.000]
 [ 2.000  4.000  6.000  8.000 10.000 12.000 14.000 16.000 18.000  20.000]
 [ 3.000  6.000  9.000 12.000 15.000 18.000 21.000 24.000 27.000  30.000]
 [ 4.000  8.000 12.000 16.000 20.000 24.000 28.000 32.000 36.000  40.000]
 [ 5.000 10.000 15.000 20.000 25.000 30.000 35.000 40.000 45.000  50.000]
 [ 6.000 12.000 18.000 24.000 30.000 36.000 42.000 48.000 54.000  60.000]
 [ 7.000 14.000 21.000 28.000 35.000 42.000 49.000 56.000 63.000  70.000]
 [ 8.000 16.000 24.000 32.000 40.000 48.000 56.000 64.000 72.000  80.000]
 [ 9.000 18.000 27.000 36.000 45.000 54.000 63.000 72.000 81.000  90.000]
 [10.000 20.000 30.000 40.000 50.000 60.000 70.000 80.000 90.000 100.000]]

Obrázek 12: Diář s operacemi s maticemi.

12. Incanter: prostředí pro statistické výpočty s grafickým výstupem založené na Clojure

Na stránce www.clojure-toolbox.com, na níž se nachází seznam nejužitečnějších a samozřejmě taktéž nejpopulárnějších knihoven a nástrojů určených pro programovací jazyk Clojure, popř. pro ClojureScript, nalezneme mj. i nástroj nazvaný Incanter. Jedná se o skupinu knihoven pro Clojure (tedy nikoli pro ClojureScript), jejichž cílem je vytvořit rychlejší a rozšiřitelnější alternativu k nástroji a programovacímu jazyku R, který se používá v oblasti statistických výpočtů, vizualizace dat apod. (a navíc se podle některých statistik stává jedním z nejvíce populárních programovacích jazyků, což je pro jazyk poměrně úzce specializovaný poněkud zvláštní). Základní informace o nepochybně zajímavém projektu Incanter získáte ze slajdů prezentace Data Sorcery with Clojure & Incanter.

Obrázek 13: Logo projektu Incanter.

Nástroj Incanter svým uživatelům nabízí zejména tyto funkce:

  1. Statistické funkce (nejenom ty základní, ale i poměrně pokročilé).
  2. Matematické funkce pro práci s vektory a maticemi (založené na již popsané knihovně clojure.core.matrix).
  3. Podporu pro tvorbu různých typů grafů a pro vizualizaci dat.
  4. Export grafů do rastrových formátů (což využijeme dnes), popř. do vektorového formátu SVG.
  5. Funkce pro manipulaci s daty a s datovými zdroji (mj. i z Excelu atd.).
  6. Základní symbolické výpočty (prozatím je podporována symbolická derivace).

Zajímavé a možná i poučné bude zjistit, z jakého důvodu vlastně projekt Incanter vznikl a proč by pro nás mohl být zajímavý, resp. proč použít Incanter a nikoli R, jazyk Julia či řešení založené na Pythonu, resp. na kombinaci Python+Numpy+SciPy). Hlavní motivací autorů tohoto projektu bylo vytvoření rozšiřitelné platformy určené pro statistické výpočty s umožněním vizualizace výsledků pomocí různých typů grafů. Tato platforma by měla uživatelům nabízet stejné či lepší vyjadřovací schopnosti než projekt R, ovšem s tím, že Incanter využije všech výhod aplikace běžící na virtuálním stroji Javy (JVM). Důvodem, proč je – minimálně slovy autorů – JVM vhodnou platformou, je mj. i to, že pro ni existuje prakticky nepřeberné množství knihoven určených pro načítání dat (použita je například knihovna xls), zpracování dat, jejich konverzi, prezentaci, přístupu k různým databázím (relační, dokumentové, objektové) atd. (ostatně přímo Incanter používá rozhraní k MongoDB pro serializaci svých datových struktur). A zapomenout nesmíme ani na velmi kvalitní klienty pro různé databáze, systém Kafka atd.

Poznámka: pokud vás zajímají některé důvody pro použití lispovského jazyka v oblasti statistických výpočtů, přečtete si článek Back to the Future: Lisp as a Base for a Statistical Computing System (opět – jedná se o více než deset let starý článek, nicméně je stále validní, i když vlastně nediskutuje to hlavní – vlastní implementaci systému založeného na Lispu).

Obrázek 14: Incanter je do značné míry alternativou ke známému nástroji R.

Se základními vlastnostmi projektu Incanter jsme se seznámili i v miniseriálu, který již na Rootu vyšel:

  1. Incanter: prostředí pro statistické výpočty s grafickým výstupem založené na Clojure
    https://www.root.cz/clanky/incanter-prostredi-pro-statisticke-vypocty-s-grafickym-vystupem-zalozene-na-clojure/
  2. Incanter: operace s maticemi
    https://www.root.cz/clanky/incanter-operace-s-maticemi/
  3. Tvorba jednoduchých grafů v systému Incanter
    https://www.root.cz/clanky/tvorba-jednoduchych-grafu-v-systemu-incanter/
  4. Tvorba grafů v systému Incanter (pokračování)
    https://www.root.cz/clanky/tvorba-grafu-v-systemu-incanter-pokracovani/

13. Instalace prostředí Incanter

Většinu běžných knihoven určených pro programovací jazyk Clojure je možné nainstalovat přímo z prostředí Jupyter Notebooku, což ovšem pro poměrně komplikovaný Incanter neplatí (resp. přesněji řečeno – teoreticky by tato instalace měla proběhnout v pořádku, ovšem při praktickém odzkoušení dojde k chybě). Proto je vhodnější vyzkoušet alternativní způsob instalace, který spočívá v tom, že příkazem:

$ lein new app incanter-app

vytvoříme kostru nové aplikace a následně upravíme projektový soubor project.clj – vložíme do něj informaci o nové závislosti:

(defproject xyz "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.8.0"]
                 [incanter "1.9.3"]]
  :main ^:skip-aot xyz.core
  :target-path "target/%s"
  :profiles {:uberjar {:aot :all}})

Pokud se navíc jedná o projekt určený současně i pro spouštění Jupyter Notebooku (což je i náš případ), bude úprava projektového souboru project.clj spočívat v přidání dvou řádků:

(defproject xyz "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.8.0"]
                 [incanter "1.9.3"]]
  :main ^:skip-aot xyz.core
  :target-path "target/%s"
  :plugins [[lein-jupyter "0.1.16"]]
  :profiles {:uberjar {:aot :all}})

Následně spustíme příkaz určený pro stažení všech závislostí:

$ lein deps
 
Retrieving incanter/incanter/1.9.3/incanter-1.9.3.pom from clojars
Retrieving incanter/incanter-core/1.9.3/incanter-core-1.9.3.pom from clojars
Retrieving org/clojure/math.combinatorics/0.1.4/math.combinatorics-0.1.4.pom from central
Retrieving net/mikera/vectorz-clj/0.44.1/vectorz-clj-0.44.1.pom from clojars
Retrieving net/mikera/clojure-pom/0.6.0/clojure-pom-0.6.0.pom from clojars
Retrieving net/mikera/mikera-pom/0.6.0/mikera-pom-0.6.0.pom from central
Retrieving net/mikera/vectorz/0.62.0/vectorz-0.62.0.pom from central
Retrieving us/bpsm/edn-java/0.4.6/edn-java-0.4.6.pom from central
...
...
...

Následně můžeme – pochopitelně z adresáře, v němž je projekt vytvořen – spustit interaktivní smyčku REPL a otestovat, zda instalace Incanteru proběhla v pořádku:

$ lein repl

Měla by se vypsat klasická uvítací obrazovka REPLu jazyka Clojure:

nREPL server started on port 42197 on host 127.0.0.1 - nrepl://127.0.0.1:42197
REPL-y 0.3.7, nREPL 0.2.12
Clojure 1.8.0
OpenJDK 64-Bit Server VM 1.8.0_191-b12
    Docs: (doc function-name-here)
          (find-doc "part-of-name-here")
  Source: (source function-name-here)
 Javadoc: (javadoc java-object-or-class-here)
    Exit: Control+D or (exit) or (quit)
 Results: Stored in vars *1, *2, *3, an exception in *e

Dále můžeme otestovat, jestli jsou na CLASSPATH umístěny všechny důležité knihovny Incanteru:

(use '(incanter core stats charts))
nil

V případě, že se skutečně (po několika sekundách) vypíše pouze nil, znamená to, že všechny tři importované knihovny je skutečně možné použít. Zkusme si tedy vykreslit například histogram náhodných hodnot s normálním rozložením:

(view (histogram (sample-normal 1000)))

Obrázek 15: Histogram náhodných hodnot s normálním rozložením.

14. Ukázky základních možností při tvorbě grafů v Incanteru

Vzhledem k tomu, že vykreslování grafů v Incanteru již bylo na stránkách Rootu popsáno (a taktéž kvůli zmenšení rozsahu tohoto článku) jsou ukázky některých typů grafů dostupné ve formě interaktivních diářů určených pro Jupyter Notebook. Diáře je možné si prohlédnout na následujících adresách, popř. je možné je přímo naimportovat do Jupyter Notebooku (ovšem v tomto případě vyžadují instalaci Clojupyteru zmíněnou v první části článku):

  1. Sloupcové grafy vykreslené funkcemi z projektu Incanter
    https://github.com/tisnik/jupyter-notebook-examples/blob/master/Clojupyter%20-%20Incanter%20charts%20%231.ipynb
  2. Základní úpravy sloupcových grafů
    https://github.com/tisnik/jupyter-notebook-examples/blob/master/Clojupyter%20-%20Incanter%20charts%20%232.ipynb
  3. Sloupcové grafy s více kategoriemi
    https://github.com/tisnik/jupyter-notebook-examples/blob/master/Clojupyter%20-%20Incanter%20charts%20%233.ipynb
  4. Zobrazení průběhů funkcí
    https://github.com/tisnik/jupyter-notebook-examples/blob/master/Clojupyter%20-%20Incanter%20charts%20%234.ipynb
  5. Vykreslení grafu s průběhem funkce
    https://github.com/tisnik/jupyter-notebook-examples/blob/master/Clojupyter%20-%20Incanter%20charts%20%235.ipynb
  6. Vykreslení funkce i její derivace
    https://github.com/tisnik/jupyter-notebook-examples/blob/master/Clojupyter%20-%20Incanter%20charts%20%236.ipynb
  7. Přidání popisků do grafu
    https://github.com/tisnik/jupyter-notebook-examples/blob/master/Clojupyter%20-%20Incanter%20charts%20%237.ipynb
Poznámka: diáře by měly být zobrazeny přímo na stránkách GitHubu, ovšem po pondělním výpadku se zdá, že některé služby stále ještě nejsou stabilní, takže se vám možná objeví pouze informace o nutnosti znovunačtení stránky.

Obrázek 16: Histogram vygenerovaný Incanterem

Obrázek 17: Bodový graf vygenerovaný Incanterem.

15. Konverze diářů do zdrojových kódů jazyka Clojure

Libovolný diář lze z Jupyter Notebooku v případě potřeby vyexportovat do dalších formátů. Můžeme se například pokusit o export do Markdownu, což bude úspěšné jen částečně, a to z toho důvodu, že standardní Markdown neobsahuje podporu pro tabulky. Ty tedy musí být vloženy pomocí HTML. Grafy jsou vyexportovány do rastrových obrázků uložených v samostatném podadresáři. Podporovány jsou i exporty do dalších formátů, včetně LaTeXu, PDF nebo AsciiDocu. Pro tyto typy exportů je však nutné mít nainstalován systém Pandoc, jehož popisu bude věnován samostatný článek.

Ovšem kromě exportu do „dokumentových“ formátů je podporován i export skriptu naprogramovaného v Clojure. V tomto případě se vyexportuje skutečně pouze programový kód zapsaný do jednotlivých buněk, který by mělo být možné spustit i mimo rámec Jupyter Notebooku:

$ jupyter nbconvert --to script Clojupyter\ -\ BufferedBitmap.ipynb
 
[NbConvertApp] Converting notebook Clojupyter - BufferedBitmap.ipynb to script
[NbConvertApp] Writing 575 bytes to Clojupyter - BufferedBitmap.clj

nebo, chcete-li:

$ jupyter nbconvert --to script "Clojupyter - BufferedBitmap.ipynb"
 
[NbConvertApp] Converting notebook Clojupyter - BufferedBitmap.ipynb to script
[NbConvertApp] Writing 575 bytes to Clojupyter - BufferedBitmap.clj

Obrázek 18: Ukázka některých základních jazykových konstrukcí jazyka Clojure.

16. Historie příkazů zapisovaných do Jupyter Notebooku

Mnoho jazyků vybavených interaktivní smyčkou REPL podporuje i uložení (a pochopitelně i znovupoužití) dříve zadaných příkazů. Je tomu tak i v případě projektu Clojupyter, který tak umožňuje velmi snadno příkazy přenášet mezi různými diáři, používat výše zmíněný příkaz use se základními moduly atd. Historie zadaných příkazů ovšem není ukládána do textových souborů (u nichž je velmi problematická sémantika – kdo, jak a kdy může historii ukládat); namísto toho se používá databáze SQLite(3) a soubor s historií je uložen přímo v domácím adresáři uživatele:

$ file .clojupyter_history 
 
.clojupyter_history: SQLite 3.x database, last written using SQLite version 3007002

Strukturu tohoto souboru si můžeme snadno prohlédnou s využitím řádkového klienta sqlite3:

$ sqlite3 .clojupyter_history
 
SQLite version 3.20.1 2017-08-24 16:21:36
Enter ".help" for usage hints.

Výpis všech databázových tabulek:

sqlite> .tables
 
history   sessions

Zobrazení schématu (struktury) obou databázových tabulek:

sqlite> .schema history
 
CREATE TABLE history (session integer, line integer, source text, primary key (session, line));
 
sqlite> .schema sessions
 
CREATE TABLE sessions (session integer primary key autoincrement, start timestamp, end timestamp, num_cmds integer default 0, remark text);

Zobrazení historie zadávaných příkazů (povšimněte si, že historie je rozdělena podle jednotlivých sezení, což je jeden z důvodů, proč by nebyl běžný textový soubor ideální):

MIF obecny

sqlite> select * from history;
 
1|1|(doc doc)
1|2|(+ 1 1)
1|3|(help help)
1|4|(doc doc)
1|5|(doc 'doc)
1|6|(print "foo")
1|7|(doc "foo")
1|8|(clojure.core/use '[clojure.repl :only (doc)])
1|9|(doc "foo")
1|10|(doc doc)
1|11|(repeat 100 1)
2|1|(clojure.core/use '[clojure.repl :only (doc)])
2|2|(require '[clojupyter.misc.display :as display])
2|3|(scatter-plot)
2|4|(display/scatter-plot)
2|5|(require '[clojupyter.display :as display])
(require '[clojupyter.misc.helper :as helper])
(helper/add-dependencies '[org.clojure/data.json "0.2.6"])
2|6|(require '[clojupyter.misc.display :as display])
(require '[clojupyter.misc.helper :as helper])
(helper/add-dependencies '[org.clojure/data.json "0.2.6"])
3|1|(doc doc)
3|2|(ns user)
3|3|(doc)
3|4|(inc 0)
3|5|(import clojure.repl)
3|6|(require clojure.repl)
3|7|(use clojure.repl)
3|8|(use 'clojure.repl)
3|9|(doc doc)
3|10|(doc if)

17. Repositář s demonstračními příklady

Všechny demonstrační příklady určené pro programovací jazyk Clojure verze 1.9.0, popř. Clojure 1.8.0, byly uloženy do repositáře, který naleznete na adrese https://github.com/tisnik/jupyter-notebook-examples:

# Notebook/skript Popis Odkaz
1 Clojupyter – empty Clojure notebook.ipynb prázdný diář nakonfigurovaný pro použití kernelu Jupyteru https://github.com/tisnik/jupyter-notebook-examples/tree/master/Clojupyter%20-%20empty%20Clojure%20notebook.ipynb
2 Clojupyter – Importing clojure.repl namespace.ipynb ukázka importu standardních balíčků, které se automaticky do diáře neimportují https://github.com/tisnik/jupyter-notebook-examples/tree/master/Clojupyter%20-%20Importing%20clojure.re­pl%20namespace.ipynb
3 Clojupyter – elementary Clojure constructs.ipynb vybrané základní konstrukce programovacího jazyka Clojure https://github.com/tisnik/jupyter-notebook-examples/tree/master/Clojupyter%20-%20elementary%20Clojure%20con­structs.ipynb
4 Clojupyter – BufferedBitmap.ipynb automatické zobrazení obsahu rastrového obrázku přímo v diáři https://github.com/tisnik/jupyter-notebook-examples/tree/master/Clojupyter%20-%20BufferedBitmap.ipynb
5 Clojupyter – Incanter basic examples.ipynb ukázky některých možností nabízených projektem Incanter https://github.com/tisnik/jupyter-notebook-examples/tree/master/Clojupyter%20-%20Incanter%20basic%20examples.ipynb
6 Clojupyter – Incanter charts #1.ipynb grafy vytvářené s využitím projektu Incanter: základní sloupcové grafy https://github.com/tisnik/jupyter-notebook-examples/tree/master/Clojupyter%20-%20Incanter%20charts%20%231.ipynb
7 Clojupyter – Incanter charts #2.ipynb grafy vytvářené s využitím projektu Incanter: úprava sloupcových grafů https://github.com/tisnik/jupyter-notebook-examples/tree/master/Clojupyter%20-%20Incanter%20charts%20%232.ipynb
8 Clojupyter – Incanter charts #3.ipynb grafy vytvářené s využitím projektu Incanter: sloupcové grafy s více kategoriemi https://github.com/tisnik/jupyter-notebook-examples/tree/master/Clojupyter%20-%20Incanter%20charts%20%233.ipynb
9 Clojupyter – Incanter charts #4.ipynb grafy vytvářené s využitím projektu Incanter: zobrazení průběhů funkcí https://github.com/tisnik/jupyter-notebook-examples/tree/master/Clojupyter%20-%20Incanter%20charts%20%234.ipynb
10 Clojupyter – Incanter charts #5.ipynb grafy vytvářené s využitím projektu Incanter: vykreslení průběhu funkce https://github.com/tisnik/jupyter-notebook-examples/tree/master/Clojupyter%20-%20Incanter%20charts%20%235.ipynb
11 Clojupyter – Incanter charts #6.ipynb grafy vytvářené s využitím projektu Incanter: vykreslení průběhu funkce i její derivace https://github.com/tisnik/jupyter-notebook-examples/tree/master/Clojupyter%20-%20Incanter%20charts%20%236.ipynb
12 Clojupyter – Incanter charts #7.ipynb grafy vytvářené s využitím projektu Incanter: přidání popisků do grafu https://github.com/tisnik/jupyter-notebook-examples/tree/master/Clojupyter%20-%20Incanter%20charts%20%237.ipynb
13 Clojupyter – introduction to clojure.core.matrix.ipynb základní použití knihovny clojure.core.matrix https://github.com/tisnik/jupyter-notebook-examples/tree/master/Clojupyter%20-%20introduction%20to%20clo­jure.core.matrix.ipynb
14 Clojupyter – other operations provided by clojure.core.matrix.ipynb vybrané operace poskytované knihovnou clojure.core.matrix https://github.com/tisnik/jupyter-notebook-examples/tree/master/Clojupyter%20-%20other%20operations%20pro­vided%20by%20clojure.core­.matrix.ipynb
15 Clojupyter – operators, functions, and macros provided by clojure.core.matrix for working with vectors.ipynb další operace poskytované knihovnou clojure.core.matrix při práci s vektory https://github.com/tisnik/jupyter-notebook-examples/tree/master/Clojupyter%20-%20operators,%20functions,%20an­d%20macros%20provided%20by%20clo­jure.core.matrix%20for%20wor­king%20with%20vectors.ipynb
16 Clojupyter – operators, functions, and macros provided by clojure.core.matrix for working with matrices.ipynb další operace poskytované knihovnou clojure.core.matrix při práci s maticemi https://github.com/tisnik/jupyter-notebook-examples/tree/master/Clojupyter%20-%20operators,%20functions,%20an­d%20macros%20provided%20by%20clo­jure.core.matrix%20for%20wor­king%20with%20matrices.ipynb
       
17 Clojupyter – empty Clojure notebook.clj prázdný diář nakonfigurovaný pro použití kernelu Jupyteru https://github.com/tisnik/jupyter-notebook-examples/tree/master/cloju­pyter/Clojupyter%20-%20empty%20Clojure%20notebook.clj
18 Clojupyter – Importing clojure.repl namespace.clj ukázka importu standardních balíčků, které se automaticky do diáře neimportují https://github.com/tisnik/jupyter-notebook-examples/tree/master/cloju­pyter/Clojupyter%20-%20Importing%20clojure.re­pl%20namespace.clj
19 Clojupyter – elementary Clojure constructs.clj vybrané základní konstrukce programovacího jazyka Clojure https://github.com/tisnik/jupyter-notebook-examples/tree/master/cloju­pyter/Clojupyter%20-%20elementary%20Clojure%20con­structs.clj
20 Clojupyter – BufferedBitmap.clj automatické zobrazení obsahu rastrového obrázku přímo v diáři https://github.com/tisnik/jupyter-notebook-examples/tree/master/cloju­pyter/Clojupyter%20-%20BufferedBitmap.clj
21 Clojupyter – Incanter basic examples.clj ukázky některých možností nabízených projektem Incanter https://github.com/tisnik/jupyter-notebook-examples/tree/master/cloju­pyter/Clojupyter%20-%20Incanter%20basic%20examples.clj
22 Clojupyter – Incanter charts #1.clj grafy vytvářené s využitím projektu Incanter: základní sloupcové grafy https://github.com/tisnik/jupyter-notebook-examples/tree/master/cloju­pyter/Clojupyter%20-%20Incanter%20charts%20%231.clj
23 Clojupyter – Incanter charts #2.clj grafy vytvářené s využitím projektu Incanter: úprava sloupcových grafů https://github.com/tisnik/jupyter-notebook-examples/tree/master/cloju­pyter/Clojupyter%20-%20Incanter%20charts%20%232.clj
24 Clojupyter – Incanter charts #3.clj grafy vytvářené s využitím projektu Incanter: sloupcové grafy s více kategoriemi https://github.com/tisnik/jupyter-notebook-examples/tree/master/cloju­pyter/Clojupyter%20-%20Incanter%20charts%20%233.clj
25 Clojupyter – Incanter charts #4.clj grafy vytvářené s využitím projektu Incanter: zobrazení průběhů funkcí https://github.com/tisnik/jupyter-notebook-examples/tree/master/cloju­pyter/Clojupyter%20-%20Incanter%20charts%20%234.clj
26 Clojupyter – Incanter charts #5.clj grafy vytvářené s využitím projektu Incanter: vykreslení průběhu funkce https://github.com/tisnik/jupyter-notebook-examples/tree/master/cloju­pyter/Clojupyter%20-%20Incanter%20charts%20%235.clj
27 Clojupyter – Incanter charts #6.clj grafy vytvářené s využitím projektu Incanter: vykreslení průběhu funkce i její derivace https://github.com/tisnik/jupyter-notebook-examples/tree/master/cloju­pyter/Clojupyter%20-%20Incanter%20charts%20%236.clj
28 Clojupyter – Incanter charts #7.clj grafy vytvářené s využitím projektu Incanter: přidání popisků do grafu https://github.com/tisnik/jupyter-notebook-examples/tree/master/cloju­pyter/Clojupyter%20-%20Incanter%20charts%20%237.clj
29 Clojupyter – introduction to clojure.core.matrix.clj základní použití knihovny clojure.core.matrix https://github.com/tisnik/jupyter-notebook-examples/tree/master/cloju­pyter/Clojupyter%20-%20introduction%20to%20clo­jure.core.matrix.clj
30 Clojupyter – other operations provided by clojure.core.matrix.clj vybrané operace poskytované knihovnou clojure.core.matrix https://github.com/tisnik/jupyter-notebook-examples/tree/master/cloju­pyter/Clojupyter%20-%20other%20operations%20pro­vided%20by%20clojure.core­.matrix.clj
31 Clojupyter – operators, functions, and macros provided by clojure.core.matrix for working with vectors.clj další operace poskytované knihovnou clojure.core.matrix při práci s vektory https://github.com/tisnik/jupyter-notebook-examples/tree/master/cloju­pyter/Clojupyter%20-%20operators,%20functions,%20an­d%20macros%20provided%20by%20clo­jure.core.matrix%20for%20wor­king%20with%20vectors.clj
32 Clojupyter – operators, functions, and macros provided by clojure.core.matrix for working with matrices.clj další operace poskytované knihovnou clojure.core.matrix při práci s maticemi https://github.com/tisnik/jupyter-notebook-examples/tree/master/cloju­pyter/Clojupyter%20-%20operators,%20functions,%20an­d%20macros%20provided%20by%20clo­jure.core.matrix%20for%20wor­king%20with%20matrices.clj

18. Odkazy na články o Jupyter Notebooku

Se samotným nástrojem Jupyter Notebook jsme se již na stránkách Rootu čtyřikrát setkali, a to konkrétně v následujících článcích:

  1. Jupyter Notebook – nástroj pro programátory, výzkumníky i lektory
    https://www.root.cz/clanky/jupyter-notebook-nastroj-pro-programatory-vyzkumniky-i-lektory/
  2. Tvorba grafů v Jupyter Notebooku s využitím knihovny Matplotlib
    https://www.root.cz/clanky/tvorba-grafu-v-jupyter-notebooku-s-vyuzitim-knihovny-matplotlib/
  3. Tvorba grafů v Jupyter Notebooku s využitím knihovny Matplotlib (dokončení)
    https://www.root.cz/clanky/tvorba-grafu-v-jupyter-notebooku-s-vyuzitim-knihovny-matplotlib-dokonceni/
  4. Jupyter Notebook – operace s rastrovými obrázky a UML diagramy, literate programming
    https://www.root.cz/clanky/jupyter-notebook-operace-s-rastrovymi-obrazky-a-uml-diagramy-literate-programming/

19. Odkazy na předchozí části seriálu o programovacím jazyku Clojure

  1. Clojure 1: Úvod
    http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm/
  2. Clojure 2: Symboly, kolekce atd.
    http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm-2-cast/
  3. 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/
  4. 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/
  5. 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/
  6. Clojure 6: Podpora pro paralelní programování
    http://www.root.cz/clanky/programovaci-jazyk-clojure-6-futures-nejsou-jen-financni-derivaty/
  7. Clojure 7: Další funkce pro paralelní programování
    http://www.root.cz/clanky/programovaci-jazyk-clojure-7-dalsi-podpurne-prostredky-pro-paralelni-programovani/
  8. 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/
  9. 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/
  10. Clojure 10: Kooperace mezi Clojure a Javou
    http://www.root.cz/clanky/programovaci-jazyk-clojure-10-kooperace-mezi-clojure-a-javou-pokracovani/
  11. Clojure 11: Generátorová notace seznamu/list comprehension
    http://www.root.cz/clanky/programovaci-jazyk-clojure-11-generatorova-notace-seznamu-list-comprehension/
  12. 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/
  13. 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/
  14. Clojure 14: Základy práce se systémem maker
    http://www.root.cz/clanky/programovaci-jazyk-clojure-14-zaklady-prace-se-systemem-maker/
  15. Clojure 15: Tvorba uživatelských maker
    http://www.root.cz/clanky/programovaci-jazyk-clojure-15-tvorba-uzivatelskych-maker/
  16. Programovací jazyk Clojure – triky při práci s řetězci
    http://www.root.cz/clanky/programovaci-jazyk-clojure-triky-pri-praci-s-retezci/
  17. Programovací jazyk Clojure – triky při práci s kolekcemi
    http://www.root.cz/clanky/programovaci-jazyk-clojure-triky-pri-praci-s-kolekcemi/
  18. Programovací jazyk Clojure – práce s mapami a množinami
    http://www.root.cz/clanky/programovaci-jazyk-clojure-prace-s-mapami-a-mnozinami/
  19. Programovací jazyk Clojure – základy zpracování XML
    http://www.root.cz/clanky/programovaci-jazyk-clojure-zaklady-zpracovani-xml/
  20. Programovací jazyk Clojure – testování s využitím knihovny Expectations
    http://www.root.cz/clanky/programovaci-jazyk-clojure-testovani-s-vyuzitim-knihovny-expectations/
  21. Programovací jazyk Clojure – některé užitečné triky použitelné (nejenom) v testech
    http://www.root.cz/clanky/programovaci-jazyk-clojure-nektere-uzitecne-triky-pouzitelne-nejenom-v-testech/
  22. Enlive – výkonný šablonovací systém pro jazyk Clojure
    http://www.root.cz/clanky/enlive-vykonny-sablonovaci-system-pro-jazyk-clojure/
  23. Nástroj Leiningen a programovací jazyk Clojure: tvorba vlastních knihoven pro veřejný repositář Clojars
    http://www.root.cz/clanky/nastroj-leiningen-a-programovaci-jazyk-clojure-tvorba-vlastnich-knihoven-pro-verejny-repositar-clojars/
  24. Novinky v Clojure verze 1.8.0
    http://www.root.cz/clanky/novinky-v-clojure-verze-1–8–0/
  25. Asynchronní programování v Clojure s využitím knihovny core.async
    http://www.root.cz/clanky/asynchronni-programovani-v-clojure-s-vyuzitim-knihovny-core-async/
  26. Asynchronní programování v Clojure s využitím knihovny core.async (pokračování)
    http://www.root.cz/clanky/asynchronni-programovani-v-clojure-s-vyuzitim-knihovny-core-async-pokracovani/
  27. Asynchronní programování v Clojure s využitím knihovny core.async (dokončení)
    http://www.root.cz/clanky/asynchronni-programovani-v-clojure-s-vyuzitim-knihovny-core-async-dokonceni/
  28. Vytváříme IRC bota v programovacím jazyce Clojure
    http://www.root.cz/clanky/vytvarime-irc-bota-v-programovacim-jazyce-clojure/
  29. Gorilla REPL: interaktivní prostředí pro programovací jazyk Clojure
    https://www.root.cz/clanky/gorilla-repl-interaktivni-prostredi-pro-programovaci-jazyk-clojure/
  30. Multimetody v Clojure aneb polymorfismus bez použití OOP
    https://www.root.cz/clanky/multimetody-v-clojure-aneb-polymorfismus-bez-pouziti-oop/
  31. Práce s externími Java archivy v programovacím jazyku Clojure
    https://www.root.cz/clanky/prace-s-externimi-java-archivy-v-programovacim-jazyku-clojure/
  32. Clojure 16: Složitější uživatelská makra
    http://www.root.cz/clanky/programovaci-jazyk-clojure-16-slozitejsi-uzivatelska-makra/
  33. Clojure 17: Využití standardních maker v praxi
    http://www.root.cz/clanky/programovaci-jazyk-clojure-17-vyuziti-standardnich-maker-v-praxi/
  34. Clojure 18: Základní techniky optimalizace aplikací
    http://www.root.cz/clanky/programovaci-jazyk-clojure-18-zakladni-techniky-optimalizace-aplikaci/
  35. Clojure 19: Vývojová prostředí pro Clojure
    http://www.root.cz/clanky/programovaci-jazyk-clojure-19-vyvojova-prostredi-pro-clojure/
  36. 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/
  37. Clojure 21: ClojureScript aneb překlad Clojure do JS
    http://www.root.cz/clanky/programovaci-jazyk-clojure-21-clojurescript-aneb-preklad-clojure-do-javascriptu/
  38. Leiningen: nástroj pro správu projektů napsaných v Clojure
    http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure/
  39. 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/
  40. 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/
  41. 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/
  42. 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/
  43. 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/
  44. Programovací jazyk Clojure a databáze (1.část)
    http://www.root.cz/clanky/programovaci-jazyk-clojure-a-databaze-1-cast/
  45. Pluginy pro Leiningen
    http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-pluginy-pro-leiningen/
  46. 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/
  47. 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/
  48. 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/
  49. 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/
  50. Seesaw: knihovna pro snadnou tvorbu GUI v jazyce Clojure
    http://www.root.cz/clanky/seesaw-knihovna-pro-snadnou-tvorbu-gui-v-jazyce-clojure/
  51. 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/
  52. 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/
  53. Programovací jazyk Clojure a práce s Gitem
    http://www.root.cz/clanky/programovaci-jazyk-clojure-a-prace-s-gitem/
  54. Programovací jazyk Clojure a práce s Gitem (2)
    http://www.root.cz/clanky/programovaci-jazyk-clojure-a-prace-s-gitem-2/
  55. 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/
  56. Pixie: lehký skriptovací jazyk s „kouzelnými“ schopnostmi
    https://www.root.cz/clanky/pixie-lehky-skriptovaci-jazyk-s-kouzelnymi-schopnostmi/
  57. Programovací jazyk Pixie: funkce ze základní knihovny a použití FFI
    https://www.root.cz/clanky/pro­gramovaci-jazyk-pixie-funkce-ze-zakladni-knihovny-a-pouziti-ffi/
  58. Novinky v Clojure verze 1.9.0
    https://www.root.cz/clanky/novinky-v-clojure-verze-1–9–0/
  59. Validace dat s využitím knihovny spec v Clojure 1.9.0
    https://www.root.cz/clanky/validace-dat-s-vyuzitim-knihovny-spec-v-clojure-1–9–0/
  60. Použití jazyka Gherkin při tvorbě testovacích scénářů pro aplikace psané v Clojure
    https://www.root.cz/clanky/pouziti-jazyka-gherkin-pri-tvorbe-testovacich-scenaru-pro-aplikace-psane-v-nbsp-clojure/
  61. Použití jazyka Gherkin při tvorbě testovacích scénářů pro aplikace psané v Clojure (2)
    https://www.root.cz/clanky/pouziti-jazyka-gherkin-pri-tvorbe-testovacich-scenaru-pro-aplikace-psane-v-nbsp-clojure-2/
  62. Incanter: prostředí pro statistické výpočty s grafickým výstupem založené na Clojure
    https://www.root.cz/clanky/incanter-prostredi-pro-statisticke-vypocty-s-grafickym-vystupem-zalozene-na-clojure/
  63. Incanter: operace s maticemi
    https://www.root.cz/clanky/incanter-operace-s-maticemi/

20. Odkazy na Internetu

  1. How to create Clojure notebooks in Jupyter
    https://s01blog.wordpress­.com/2017/12/10/how-to-create-clojure-notebooks-in-jupyter/
  2. Dokumentace k nástroji Conda
    https://docs.conda.io/en/latest/
  3. Notebook interface
    https://en.wikipedia.org/wi­ki/Notebook_interface
  4. Jypyter: open source, interactive data science and scientific computing across over 40 programming languages
    https://jupyter.org/
  5. Calysto Scheme
    https://github.com/Calysto/ca­lysto_scheme
  6. scheme.py (základ projektu Calysto Scheme)
    https://github.com/Calysto/ca­lysto_scheme/blob/master/ca­lysto_scheme/scheme.py
  7. Matplotlib Home Page
    http://matplotlib.org/
  8. Matplotlib (Wikipedia)
    https://en.wikipedia.org/wi­ki/Matplotlib
  9. Popis barvových map modulu matplotlib.cm
    https://gist.github.com/en­dolith/2719900#id7
  10. Ukázky (palety) barvových map modulu matplotlib.cm
    http://matplotlib.org/exam­ples/color/colormaps_refe­rence.html
  11. Galerie grafů vytvořených v Matplotlibu
    https://matplotlib.org/3.2.1/gallery/
  12. showcase example code: xkcd.py
    https://matplotlib.org/xkcd/e­xamples/showcase/xkcd.html
  13. Customising contour plots in matplotlib
    https://philbull.wordpres­s.com/2012/12/27/customising-contour-plots-in-matplotlib/
  14. Graphics with Matplotlib
    http://kestrel.nmt.edu/~ra­ymond/software/python_notes/pa­per004.html
  15. The IPython Notebook
    http://ipython.org/notebook.html
  16. nbviewer: a simple way to share Jupyter Notebooks
    https://nbviewer.jupyter.org/
  17. Back to the Future: Lisp as a Base for a Statistical Computing System
    https://www.stat.auckland­.ac.nz/~ihaka/downloads/Com­pstat-2008.pdf
  18. gg4clj: a simple wrapper for using R's ggplot2 in Clojure and Gorilla REPL
    https://github.com/JonyEpsilon/gg4clj
  19. Analemma: a Clojure-based SVG DSL and charting library
    http://liebke.github.io/analemma/
  20. Clojupyter: a Jupyter kernel for Clojure
    https://github.com/roryk/clojupyter
  21. Incanter is a Clojure-based, R-like platform for statistical computing and graphics.
    http://incanter.org/
  22. Evolution of incanter (Gource Visualization)
    https://www.youtube.com/wat­ch?v=TVfL5nPELr4
  23. Questions tagged [incanter] (na Stack Overflow)
    https://stackoverflow.com/qu­estions/tagged/incanter?sor­t=active
  24. Data Sorcery with Clojure
    https://data-sorcery.org/contents/
  25. What is REPL?
    https://pythonprogramminglan­guage.com/repl/
  26. What is a REPL?
    https://codewith.mu/en/tu­torials/1.0/repl
  27. Programming at the REPL: Introduction
    https://clojure.org/guides/re­pl/introduction
  28. What is REPL? (Quora)
    https://www.quora.com/What-is-REPL
  29. Gorilla REPL: interaktivní prostředí pro programovací jazyk Clojure
    https://www.root.cz/clanky/gorilla-repl-interaktivni-prostredi-pro-programovaci-jazyk-clojure/
  30. R Markdown: The Definitive Guide
    https://bookdown.org/yihui/rmarkdown/
  31. Single-page application
    https://en.wikipedia.org/wiki/Single-page_application
  32. Video streaming in the Jupyter Notebook
    https://towardsdatascience.com/video-streaming-in-the-jupyter-notebook-635bc5809e85
  33. How IPython and Jupyter Notebook work
    https://jupyter.readthedoc­s.io/en/latest/architectu­re/how_jupyter_ipython_wor­k.html
  34. Jupyter kernels
    https://github.com/jupyter/ju­pyter/wiki/Jupyter-kernels
  35. Keras: The Python Deep Learning library
    https://keras.io/
  36. TensorFlow
    https://www.tensorflow.org/
  37. PyTorch
    https://pytorch.org/
  38. Seriál Torch: framework pro strojové učení
    https://www.root.cz/serialy/torch-framework-pro-strojove-uceni/
  39. Scikit-learn
    https://scikit-learn.org/stable/
  40. Java Interop (Clojure)
    https://clojure.org/referen­ce/java_interop
  41. Obrazy s balíčky Jupyter Notebooku pro Docker
    https://hub.docker.com/u/jupyter/#!
  42. Správce balíčků Conda (dokumentace)
    https://docs.conda.io/en/latest/
  43. Lorenzův atraktor
    https://www.root.cz/clanky/fraktaly-v-pocitacove-grafice-vi/#k02
  44. Lorenzův atraktor
    https://www.root.cz/clanky/fraktaly-v-pocitacove-grafice-iii/#k03
  45. Graphics with Matplotlib
    http://kestrel.nmt.edu/~ra­ymond/software/python_notes/pa­per004.html
  46. Embedding Matplotlib Animations in Jupyter Notebooks
    http://louistiao.me/posts/no­tebooks/embedding-matplotlib-animations-in-jupyter-notebooks/
  47. Literate programing, Kolokviální práce Pavla Starého
    https://www.fi.muni.cz/us­r/jkucera/pv109/starylp.htm
  48. PlantUML (home page)
    http://plantuml.sourceforge.net/
  49. PlantUML (download page)
    http://sourceforge.net/pro­jects/plantuml/files/plan­tuml.jar/download
  50. PlantUML (Language Reference Guide)
    http://plantuml.sourcefor­ge.net/PlantUML_Language_Re­ference_Guide.pdf
  51. Plain-text diagrams take shape in Asciidoctor!
    http://asciidoctor.org/new­s/2014/02/18/plain-text-diagrams-in-asciidoctor/
  52. Graphviz – Graph Visualization Software
    http://www.graphviz.org/
  53. graphviz (Manual Page)
    http://www.root.cz/man/7/graphviz/
  54. PIL: The friendly PIL fork (home page)
    https://python-pillow.org/
  55. Python Imaging Library (PIL), (home page)
    http://www.pythonware.com/pro­ducts/pil/
  56. PIL 1.1.6 na PyPi
    https://pypi.org/project/PIL/
  57. Pillow 5.2.0 na PyPi
    https://pypi.org/project/Pillow/
  58. Python Imaging Library na Wikipedii
    https://en.wikipedia.org/wi­ki/Python_Imaging_Library
  59. Pillow na GitHubu
    https://github.com/python-pillow/Pillow
  60. Pillow – dokumentace na readthedocs.io
    http://pillow.readthedocs­.io/en/5.2.x/
  61. How to use Pillow, a fork of PIL
    https://www.pythonforbegin­ners.com/gui/how-to-use-pillow
  62. Humane test output for clojure.test
    https://github.com/pjstadig/humane-test-output
  63. iota
    https://github.com/juxt/iota
  64. 5 Differences between clojure.spec and Schema
    https://lispcast.com/clojure.spec-vs-schema/
  65. Schema: Clojure(Script) library for declarative data description and validation
    https://github.com/plumatic/schema
  66. Zip archiv s Clojure 1.9.0
    http://repo1.maven.org/ma­ven2/org/clojure/clojure/1­.9.0/clojure-1.9.0.zip
  67. Clojure 1.9 is now available
    https://clojure.org/news/2017/12/08/clo­jure19
  68. Deps and CLI Guide
    https://clojure.org/guides/dep­s_and_cli
  69. Changes to Clojure in Version 1.9
    https://github.com/clojure/clo­jure/blob/master/changes.md
  70. clojure.spec – Rationale and Overview
    https://clojure.org/about/spec
  71. Zip archiv s Clojure 1.8.0
    http://repo1.maven.org/ma­ven2/org/clojure/clojure/1­.8.0/clojure-1.8.0.zip
  72. Clojure 1.8 is now available
    http://clojure.org/news/2016/01/19/clo­jure18
  73. Socket Server REPL
    http://dev.clojure.org/dis­play/design/Socket+Server+REPL
  74. CLJ-1671: Clojure socket server
    http://dev.clojure.org/jira/browse/CLJ-1671
  75. CLJ-1449: Add clojure.string functions for portability to ClojureScript
    http://dev.clojure.org/jira/browse/CLJ-1449
  76. Launching a Socket Server
    http://clojure.org/referen­ce/repl_and_main#_launchin­g_a_socket_server
  77. API for clojure.string
    http://clojure.github.io/clo­jure/branch-master/clojure.string-api.html
  78. Clojars:
    https://clojars.org/
  79. Seznam knihoven na Clojars:
    https://clojars.org/projects
  80. Clojure Cookbook: Templating HTML with Enlive
    https://github.com/clojure-cookbook/clojure-cookbook/blob/master/07_webapps/7–11_enlive.asciidoc
  81. An Introduction to Enlive
    https://github.com/swannodette/enlive-tutorial/
  82. Enlive na GitHubu
    https://github.com/cgrand/enlive
  83. Expectations: příklady atd.
    http://jayfields.com/expectations/
  84. Expectations na GitHubu
    https://github.com/jaycfi­elds/expectations
  85. Lein-expectations na GitHubu
    https://github.com/gar3thjon3s/lein-expectations
  86. Testing Clojure With Expectations
    https://semaphoreci.com/blog/2014/09/23/tes­ting-clojure-with-expectations.html
  87. Clojure testing TDD/BDD libraries: clojure.test vs Midje vs Expectations vs Speclj
    https://www.reddit.com/r/Clo­jure/comments/1viilt/cloju­re_testing_tddbdd_librari­es_clojuretest_vs/
  88. Testing: One assertion per test
    http://blog.jayfields.com/2007/06/tes­ting-one-assertion-per-test.html
  89. Rewriting Your Test Suite in Clojure in 24 hours
    http://blog.circleci.com/rewriting-your-test-suite-in-clojure-in-24-hours/
  90. Clojure doc: zipper
    http://clojuredocs.org/clo­jure.zip/zipper
  91. Clojure doc: parse
    http://clojuredocs.org/clo­jure.xml/parse
  92. Clojure doc: xml-zip
    http://clojuredocs.org/clojure.zip/xml-zip
  93. Clojure doc: xml-seq
    http://clojuredocs.org/clo­jure.core/xml-seq
  94. Parsing XML in Clojure
    https://github.com/clojuredocs/guides
  95. Clojure Zipper Over Nested Vector
    https://vitalyper.wordpres­s.com/2010/11/23/clojure-zipper-over-nested-vector/
  96. Understanding Clojure's PersistentVector implementation
    http://blog.higher-order.net/2009/02/01/understanding-clojures-persistentvector-implementation
  97. Understanding Clojure's PersistentHashMap (deftwice…)
    http://blog.higher-order.net/2009/09/08/understanding-clojures-persistenthashmap-deftwice.html
  98. Assoc and Clojure's PersistentHashMap: part ii
    http://blog.higher-order.net/2010/08/16/assoc-and-clojures-persistenthashmap-part-ii.html
  99. Ideal Hashtrees (paper)
    http://lampwww.epfl.ch/pa­pers/idealhashtrees.pdf
  100. Clojure home page
    http://clojure.org/
  101. Clojure (downloads)
    http://clojure.org/downloads
  102. Clojure Sequences
    http://clojure.org/sequences
  103. Clojure Data Structures
    http://clojure.org/data_structures
  104. 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
  105. 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
  106. Clojure – Functional Programming for the JVM
    http://java.ociweb.com/mar­k/clojure/article.html
  107. Clojure quick reference
    http://faustus.webatu.com/clj-quick-ref.html
  108. 4Clojure
    http://www.4clojure.com/
  109. ClojureDoc (rozcestník s dokumentací jazyka Clojure)
    http://clojuredocs.org/
  110. Clojure (na Wikipedia EN)
    http://en.wikipedia.org/wiki/Clojure
  111. Clojure (na Wikipedia CS)
    http://cs.wikipedia.org/wiki/Clojure
  112. SICP (The Structure and Interpretation of Computer Programs)
    http://mitpress.mit.edu/sicp/
  113. Pure function
    http://en.wikipedia.org/wi­ki/Pure_function
  114. Funkcionální programování
    http://cs.wikipedia.org/wi­ki/Funkcionální_programová­ní
  115. Čistě funkcionální (datové struktury, jazyky, programování)
    http://cs.wikipedia.org/wi­ki/Čistě_funkcionální
  116. 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
  117. Clojure Macro Tutorial (Part II: The Compiler Strikes Back)
    http://www.learningclojure­.com/2010/09/clojure-macro-tutorial-part-ii-compiler.html
  118. Clojure Macro Tutorial (Part III: Syntax Quote)
    http://www.learningclojure­.com/2010/09/clojure-macro-tutorial-part-ii-syntax.html
  119. Tech behind Tech: Clojure Macros Simplified
    http://techbehindtech.com/2010/09/28/clo­jure-macros-simplified/
  120. Fatvat – Exploring functional programming: Clojure Macros
    http://www.fatvat.co.uk/2009/02/clo­jure-macros.html
  121. Eulerovo číslo
    http://cs.wikipedia.org/wi­ki/Eulerovo_číslo
  122. List comprehension
    http://en.wikipedia.org/wi­ki/List_comprehension
  123. List Comprehensions in Clojure
    http://asymmetrical-view.com/2008/11/18/list-comprehensions-in-clojure.html
  124. Clojure Programming Concepts: List Comprehension
    http://en.wikibooks.org/wi­ki/Clojure_Programming/Con­cepts#List_Comprehension
  125. Clojure core API: for macro
    http://clojure.github.com/clo­jure/clojure.core-api.html#clojure.core/for
  126. cirrus machina – The Clojure for macro
    http://www.cirrusmachina.com/blog/com­ment/the-clojure-for-macro/
  127. Riastradh's Lisp Style Rules
    http://mumble.net/~campbe­ll/scheme/style.txt
  128. Dynamic Languages Strike Back
    http://steve-yegge.blogspot.cz/2008/05/dynamic-languages-strike-back.html
  129. Scripting: Higher Level Programming for the 21st Century
    http://www.tcl.tk/doc/scripting.html
  130. Java Virtual Machine Support for Non-Java Languages
    http://docs.oracle.com/ja­vase/7/docs/technotes/gui­des/vm/multiple-language-support.html
  131. Třída java.lang.String
    http://docs.oracle.com/ja­vase/7/docs/api/java/lang/Strin­g.html
  132. Třída java.lang.StringBuffer
    http://docs.oracle.com/ja­vase/7/docs/api/java/lang/Strin­gBuffer.html
  133. Třída java.lang.StringBuilder
    http://docs.oracle.com/ja­vase/7/docs/api/java/lang/Strin­gBuilder.html
  134. StringBuffer versus String
    http://www.javaworld.com/ar­ticle/2076072/build-ci-sdlc/stringbuffer-versus-string.html
  135. Threading macro (dokumentace k jazyku Clojure)
    https://clojure.github.io/clo­jure/clojure.core-api.html#clojure.core/->
  136. Understanding the Clojure → macro
    http://blog.fogus.me/2009/09/04/un­derstanding-the-clojure-macro/
  137. clojure.inspector
    http://clojure.github.io/clo­jure/clojure.inspector-api.html
  138. The Clojure Toolbox
    http://www.clojure-toolbox.com/
  139. Unit Testing in Clojure
    http://nakkaya.com/2009/11/18/unit-testing-in-clojure/
  140. Testing in Clojure (Part-1: Unit testing)
    http://blog.knoldus.com/2014/03/22/tes­ting-in-clojure-part-1-unit-testing/
  141. API for clojure.test – Clojure v1.6 (stable)
    https://clojure.github.io/clo­jure/clojure.test-api.html
  142. Leiningen: úvodní stránka
    http://leiningen.org/
  143. Leiningen: Git repository
    https://github.com/techno­mancy/leiningen
  144. leiningen-win-installer
    http://leiningen-win-installer.djpowell.net/
  145. Clojure.org: Vars and the Global Environment
    http://clojure.org/Vars
  146. Clojure.org: Refs and Transactions
    http://clojure.org/Refs
  147. Clojure.org: Atoms
    http://clojure.org/Atoms
  148. Clojure.org: Agents as Asynchronous Actions
    http://clojure.org/agents
  149. Transient Data Structureshttp://clojure.or­g/transients