Hlavní navigace

Programová tvorba diagramů v jazyku Clojure s využitím knihovny Rhizome

13. 5. 2021
Doba čtení: 27 minut

Sdílet

 Autor: Clojure
V novém miniseriálu si popíšeme nástroje, které dokážou vytvářet různé typy grafů a diagramů přímo ze zdrojových kódů vybraného programovacího jazyka. To tedy znamená, že konkrétní jazyk bude využit namísto specializovaného DSL.

Obsah

1. Programová tvorba diagramů v jazyku Clojure s využitím knihovny Rhizome

2. Nástroj Rhizome spojující možnosti jazyka Clojure a nástroje Graphviz

3. Balíček vizualizačních nástrojů Graphviz

4. Základní typy grafů podporovaných nástrojem Graphviz

5. Instalace knihovny Rhizome s využitím nástroje Leiningen

6. Vyzkoušení základních možností projektu Rhizome z interaktivní smyčky REPL

7. Zobrazení grafů

8. Koncové uzly grafu

9. Složitější graf, v němž mají uzly více potomků

10. Specifikace stylu vykreslení grafu

11. Popisky hran v grafu

12. Modifikace barvy uzlu, popř. stylu vykreslení

13. Modifikace stylu vykreslení hran

14. Shlukování uzlů (clustering)

15. Konkrétní specifikace shlukovaných uzlů

16. Kombinace předchozích příkladů

17. Repositář s demonstračním příkladem

18. Odkazy na články s tématem programové tvorby grafů a diagramů

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

20. Odkazy na Internetu

1. Programová tvorba diagramů v jazyku Clojure s využitím knihovny Rhizome

Na stránkách Roota jsme se již několikrát setkali s nástroji určenými pro tvorbu různých typů grafů a diagramů (viz též osmnáctou kapitolu s odkazy na tyto články). V naprosté většině případů byla deklarace grafu provedena v nějakém doménově specifickém jazyce neboli DSL (Domain Specific Language). Připomeňme si, že se jednalo například o nástroje Graphviz (což je ve skutečnosti sada několika relativně samostatných nástrojů), dále o nástroj Ditaa pro přidání grafů a diagramů do dokumentů tvořených v Asciidocu, a zapomenout nesmíme ani na propracovaný nástroj plantUML, který podporuje hned několik typů diagramů, přičemž pro každý diagram je použit poněkud odlišný DSL. Do této kategorie může spadat i GNUplot.

Obrázek 1: Diagram vytvořený aplikací Ditaa.

Doménově specifické jazyky jsou velmi důležitou součástí informatiky a mnohé z nich jsou velmi úspěšné a rozšířené do mnoha oblastí. Za připomenutí stojí například jazyk pro popis regulárních výrazů, jenž je podporován jak mnoha nástroji, tak i knihovnami, popř. je přímo součástí některých obecných programovacích jazyků (Perl apod.). I pro popisy grafů a diagramů jsou DSL obecně velmi dobrou volbou, ovšem na druhou stranu je nutné podotknout, že pro určité použití je výhodnější nechat si graf či diagram vykreslit přímo na základě zdrojových kódů nějakého obecného (resp. přesněji řečeno univerzálního) programovacího jazyka. Velmi dobrým příkladem jsou například vývojové diagramy (flow chart), jenž mohou být užitečným komunikačním prostředkem mezi vývojovým týmem a zákazníkem, ovšem jejich ruční tvorba v DSL může být zdlouhavá a navíc i neintuitivní – výhodnější v tomto případě bude vytvoření vývojového diagramu přímo ze zdrojového kódu (což umožňuje například nástroj PyFlowchart, jímž se budeme zabývat příště).

Obrázek 2: Další diagram vytvořený aplikací Ditaa.

2. Nástroj Rhizome spojující možnosti jazyka Clojure a nástroje Graphviz

Dnes se seznámíme s velmi jednoduše použitelným nástrojem určeným pro vizualizaci grafů. Tento nástroj se jmenuje Rhizome (česky oddenek) a jeho primárním jazykem pro popis grafů není specializovaný DSL, ale univerzální programovací jazyk Clojure (to tedy znamená, že náš seriál začneme zdánlivě méně praktickým jazykem, v mnoha oblastech je však opak pravdou, i když Clojure stále nepatří mezi mainstream). Nástroj Rhizome, který bude popsán, používá pro vizualizaci grafů Linuxový nástroj Graphviz, ovšem nepřímo – uživatel-programátor nebude muset přímo s Graphviz pracovat ani se učit jeho DSL (který ovšem není pro většinu běžných typů grafů příliš složitý – jeden příklad psaný přímo pro Graphviz si ostatně ukážeme později).

Obrázek 3: Tento obrázek je použit jako logo nástroje Rhizome.
Zdroj: https://github.com/ztellman/rhizome

Nástroj Rhizome dokáže pracovat s mnoha typy grafů; ovšem základní dělení je na grafy s orientovanými hranami a na grafy s hranami neorientovanými. Graf je primárně reprezentován datovou strukturou programovacího jazyka Clojure (typicky nějakou formou mapy) a následně je interně převeden do doménově specifického jazyka nástroje Graphviz. Dále je Graphviz zavolán a výsledek je zobrazen v nově vytvořeném okně, popř. je uložen na disk ve formě rastrového obrázku, dokumentu PDF, vektorové kresby v PostScriptu atd.

Poznámka: tím, že je graf (tedy uzly a hrany) reprezentován běžnou mapou to v důsledku znamená, že se může jednat o datový soubor uložený ve formátu EDN.

Obrázek 4: Diagram aktivit vygenerovaný nástrojem plantUML. Ve výchozím nastavení jsou uzly umístěné pod sebe a šipky mezi nimi směřují vertikálně dolů (v případě, že není použito rozvětvení, kterým se budeme zabývat níže).

Jaké jsou přednosti tohoto řešení? Především je možné stále pracovat pouze v Clojure bez nutnosti znát interní jazyk nástroje Graphviz. Dále se – díky homoikonicitě programovacího jazyka Clojure – mohou namísto statických dat představujících uzly grafu a relace mezi uzly použít přímo funkce, které údaje o grafu dynamicky generují. A konečně je možné Rhizome relativně snadno navázat na celou další sadu nástrojů vytvořených pro Clojure a například využít možnosti generování grafu se závislostmi a tranzitivními závislostmi volaného přímo z nástroje Leiningen (atd. atd.).

Obrázek 5: PlantUML – jednoduché rozvětvení reprezentované v diagramu aktivit.

3. Balíček vizualizačních nástrojů Graphviz

V mnoha dokumentech, popř. v různých reportech (někdy i automaticky generovaných) je nutné vytvářet diagramy s uzly propojenými neorientovanými či orientovanými hranami. Pro tento účel je vhodné ve většině případů použít balíček nástrojů nazvaný Graphviz. V tomto balíčku nalezneme především utilitu nazvanou dot, která na základě textové definice orientovaného či neorientovaného grafu vytvoří rastrový či vektorový obrázek s grafem, přičemž je možné zvolit, jaký algoritmus bude použit pro rozmístění uzlů a hran na vytvořeném obrázku. Modifikovat lze i další vlastnosti grafu, především styl vykreslení hran, rozmístění uzlů grafu apod. Textová definice grafu používá jednoduchý popisný jazyk (samozřejmě doménově specifický), který je v současnosti podporován i několika dalšími nástroji a stává se tak nepsaným standardem pro mnoho programů pracujících s grafovými strukturami.

Obrázek 6: Nástroj Graphviz lze použít i pro vizualizaci objektů uložených v operační paměti (Python).

4. Základní typy grafů podporovaných nástrojem Graphviz

Podívejme se nyní na velmi jednoduchý demonstrační příklad – konkrétně na graf se třemi uzly a čtyřmi hranami (spojnicemi) mezi těmito uzly. Takový graf může být popsán v textovém souboru s následující strukturou:

Obsah souboru graph1.dot:

graph languages {
    Algol -- "K&R C";
    "K&R C" -- "ANSI C";
    Algol -- Perl;
    Algol -- "PL/I";
}

Povšimněte si, že u některých uzlů bylo zapotřebí uzavřít jejich názvy do uvozovek. Není těžké zjistit proč – objevují se zde totiž znaky, které by mohly být interpretovány odlišným způsobem (ampersand, mezera, lomítko) a uzavřením do uvozovek se (většina) těchto problémů automaticky vyřeší. Celý soubor začíná klíčovým slovem graph značící neorientovaný graf; ve skutečnosti je však možné celou hlavičku bloku vynechat (což ostatně dělají i některé nástroje), nebo naopak do hlavičky doplnit další informace o stylu zobrazení grafu. Neorientované hrany mezi jednotlivými uzly jsou naznačeny dvojicí znaků „–“ a jak uvidíme v následujícím odstavci, bude tato značka u orientovaných grafů nahrazena šipkou (což je dobrá mnemotechnická pomůcka).

O automatické rozmístění uzlů i hran a následné vykreslení diagramu do rastrového obrázku s formátem PNG se postará tento příkaz:

$ dot graph1.dot -T png & graph1.png

Výsledek si můžete prohlédnout na dalším obrázku zobrazeném pod tímto odstavcem. Příkaz dot v tomto případě vytvořil klasický strom s kořenem Algol, což nám v tomto případě zcela vyhovuje.

Obrázek 7: Neorientovaný graf vytvořený na základě definičního souboru graph1.dot popsaného výše.

Zajímavé je, že příkaz dot zapisuje obrázek (či popis vektorového výkresu) na standardní výstup, takže je možné použít přesměrování standardního výstupu do souboru. Alternativní výstupní formáty jsou svg, gif, jpeg a ps (PostScript):

$ dot graph1.dot -Tps & graph1.ps
$ dot graph1.dot -Tsvg & graph1.svg
$ dot graph1.dot -Tgif & graph1.gif
$ dot graph1.dot -Tjpeg & graph1.jpeg

Kromě příkazu dot lze použít i příkazy neato, fdp či sfdp, které se odlišují algoritmem použitým pro rozmístění uzlů diagramu.

Podívejme se nyní na tvorbu grafu, v němž jsou neorientované hrany mezi uzly nahrazeny šipkami, tedy běžnými orientovanými hranami. Použijeme přitom stejné uzly (přesněji řečeno uzly se stejným textem), jako tomu bylo i v předchozím demonstračním příkladu. Změn v definičním textovém (zdrojovém) souboru je jen několik. První změnu můžeme vidět již na prvním řádku, protože se namísto klíčového slova graph používá slovo digraph. Dále se pak na dalších řádcích nahradila dvojice znaků „–“ za šipku „->“ naznačující přímo směr hrany:

Obsah souboru graph2.dot:

digraph languages {
    Algol -> "K&R C";
    "K&R C" -> "ANSI C";
    Algol -> Perl;
    Algol -> "PL/I";
}

Obrázek 8: Orientovaný graf vytvořený na základě definičního souboru graph2.dot.

U uzlů a hran je možné specifikovat různé vlastnosti a ovlivnit tak výsledné zobrazení grafu. Podívejme se na jednoduchou úpravu spočívající v tom, že se nejdříve deklaruje styl dvou uzlů (tvar uzlu + jeho barva) a následně se tyto uzly použijí pro specifikaci topologie grafu:

Obsah souboru graph3.dot:

digraph languages {
    Algol [shape=box];
    Algol [color="#ff0000"];
    "ANSI C" [color="#00ff00"];
    Algol -> "K&R C";
    "K&R C" -> "ANSI C";
    Algol -> Perl;
    Algol -> "PL/I";
}

Obrázek 9: Orientovaný graf vytvořený na základě definičního souboru graph3.dot.

Poznámka: existuje mnoho dalších voleb ovlivňujících styl vykreslení grafu; s některými se setkáme v navazujících kapitolách.

5. Instalace knihovny Rhizome s využitím nástroje Leiningen

Po krátké odbočce se vraťme k programovacímu jazyku Clojure a nástroji Rhizome.

Nástroj Rhizome je dodáván formou knihovny programovacího jazyka Clojure a lze ho tedy nainstalovat například s využitím nástroje Leiningen, s nímž jsme se již na stránkách Roota taktéž seznámili (viz odkazy v devatenácté kapitole). Pro instalaci nejdříve vytvoříme nový prázdný projekt naprogramovaný v jazyku Clojure. Pro vytvoření nového projektu použijeme tento příkaz:

$ lein new app rhizome-1

Výsledkem by měla být následující adresářová struktura:

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

Nyní do projektového souboru, jenž se jmenuje project.clj, doplníme nový balíček, na němž projekt závisí. Původní podoba projektového souboru může vypadat následovně (může se lišit podle nainstalované verze jazyka Clojure):

(defproject rhizome-1 "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
            :url "https://www.eclipse.org/legal/epl-2.0/"}
  :dependencies [[org.clojure/clojure "1.10.1"]]
  :main ^:skip-aot rhizome-1.core
  :target-path "target/%s"
  :profiles {:uberjar {:aot :all
                       :jvm-opts ["-Dclojure.compiler.direct-linking=true"]}})

Tento soubor upravíme následovně (změna je zvýrazněna tučným písmem):

(defproject rhizome-1 "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
            :url "https://www.eclipse.org/legal/epl-2.0/"}
  :dependencies [[org.clojure/clojure "1.10.1"]
                 [rhizome "0.2.9"]]
  :main ^:skip-aot rhizome-1.core
  :target-path "target/%s"
  :profiles {:uberjar {:aot :all
                       :jvm-opts ["-Dclojure.compiler.direct-linking=true"]}})

Nyní je již vše připraveno pro instalaci nástroje Rhizome. K tomu použijeme tento příkaz:

$ lein deps
 
Retrieving rhizome/rhizome/0.2.9/rhizome-0.2.9.pom from clojars
Retrieving rhizome/rhizome/0.2.9/rhizome-0.2.9.jar from clojars
Poznámka: všechny závislosti (tedy i ty tranzitivní) tohoto projektu vypadají následovně:
{[clojure-complete "0.2.5" :exclusions [[org.clojure/clojure]]] nil,
 [nrepl "0.7.0" :exclusions [[org.clojure/clojure]]] nil,
 [org.clojure/clojure "1.10.1"]
 {[org.clojure/core.specs.alpha "0.2.44"] nil,
  [org.clojure/spec.alpha "0.2.176"] nil},
 [rhizome "0.2.9"] nil,
 [venantius/ultra "0.6.0"]
 {[grimradical/clj-semver "0.3.0" :exclusions [[org.clojure/clojure]]]
  nil,
  [io.aviso/pretty "0.1.35"] nil,
  [mvxcvi/puget "1.1.0"]
  {[fipp "0.6.14"] {[org.clojure/core.rrb-vector "0.0.13"] nil},
   [mvxcvi/arrangement "1.1.1"] nil},
  [mvxcvi/whidbey "2.1.0"] {[org.clojure/data.codec "0.1.1"] nil},
  [org.clojars.brenton/google-diff-match-patch "0.1"] nil,
  [robert/hooke "1.3.0"] nil,
  [venantius/glow "0.1.5" :exclusions [[hiccup] [garden]]]
  {[clj-antlr "0.2.3"]
   {[org.antlr/antlr4-runtime "4.5.3"] nil,
    [org.antlr/antlr4 "4.5.3"] nil},
   [instaparse "1.4.1"] nil}}}

6. Vyzkoušení základních možností projektu Rhizome z interaktivní smyčky REPL

Samotný nástroj Rhizome lze pochopitelně otestovat jednoduše takovým způsobem, že se bude upravovat zdrojový kód projektu (src/rhizome1/core.clj, například takto:

(ns rhizome-1.core)
 
(require '[rhizome.viz :as viz])
 
 
(defn -main
  [& args]
  (let [graph {:a [:b :c]
               :b [:c]
               :c [:a]}]
    (viz/view-graph (keys graph) graph
                    :node->descriptor (fn [n] {:label n}))))

A následně se bude projekt opakovaně spouštět příkazem:

$ lein run

S výsledkem:

Obrázek 10: Výsledek předchozího příkladu.

To však není – alespoň ne v případě programovacího jazyka Clojure – ten nejlepší možný způsob, protože inicializace Clojure i virtuálního stroje Javy není v žádném případě okamžitá. Namísto toho je výhodnější spustit interaktivní smyčku REPL (Read-eval-print loop) a možnosti nástroje Rhizome zkoušet interaktivně s možností návratu k předchozím datům či příkazům atd. A pravděpodobně nejvýhodnější je mít spuštěnu jak smyčku REPL, tak i váš oblíbený programátorský textový editor či vývojové prostředí, které s REPL dokáže komunikovat (příklady jsme si již ukazovali pro Vim, pro Emacs lze použít Cider – viz například tento odkaz). Spusťme si tedy smyčku REPL, a to přímo v tom adresáři, v němž se nachází výše zmíněný projekt:

$ lein repl

Po několika sekundách by se měla objevit zpráva o spuštění a inicializaci prostředí jazyka Clojure:

nREPL server started on port 43485 on host 127.0.0.1 - nrepl://127.0.0.1:43485
REPL-y 0.4.4, nREPL 0.7.0
Clojure 1.10.1
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
 
rhizome-1.core=>

Obrázek 11: Kombinace REPLu jazyka Clojure a programátorského textového editoru. Jednotlivé formy se do REPLu posílají přes tmux.

Nyní je nutné knihovnu Rhizome načíst. To lze provést několika způsoby – buď s využitím formy use nebo pomocí formy require. Ve druhém případě je možné specifikovat jmenný alias, který bude použit:

rhizome-1.core=> (use 'rhizome.dot)
rhizome-1.core=> (use 'rhizome.viz)

popř.:

rhizome-1.core=> (require '[rhizome.dot :as dot])
rhizome-1.core=> (require '[rhizome.viz :as viz])
Poznámka: v dalším textu budeme předpokládat, že je použit druhý způsob, který je v mnoha ohledech „čistější“, protože se nesnaží do aktuálně používaného jmenného prostoru přidávat další symboly, které mohou potenciálně kolidovat se symboly existujícími.

7. Zobrazení grafů

Pro zobrazení grafů v novém okně (vytvořeném přímo v prostředí virtuálního stroje Javy) se používá funkce nazvaná view-graph ze jmenného prostoru rhizome.viz:

rhizome-1.core=> (doc viz/view-graph)
-------------------------
rhizome.viz/view-graph
([nodes adjacent & {:keys [directed? vertical? options node->descriptor
edge->descriptor cluster->parent node->cluster
cluster->descriptor], :or {directed? true, vertical? true,
node->descriptor (constantly nil), edge->descriptor (constantly nil),
cluster->parent (constantly nil), node->cluster (constantly nil),
cluster->descriptor (constantly nil)}, :as graph-descriptor}])
  Takes a graph descriptor in the style of `graph->dot`, and displays a rendered image.

Alternativně je možné graf uložit do souboru (typicky do rastrového obrázku) funkcí rhizome.viz/save-graph:

rhizome-1.core=> (doc viz/save-graph)
-------------------------
rhizome.viz/save-graph
([nodes adjacent & {:keys [filename], :as options}])
  Takes a graph descriptor in the style of `graph->dot`, and saves the image to disk.
Poznámka: obě výše zmíněné funkce akceptují prakticky totožné parametry; jediný podstatný rozdíl spočívá v tom, že save-graph akceptuje i jméno výstupního souboru s obrázkem grafu.

Kromě toho je možné graf převést do souboru .dot, který je dále zpracovatelný příkazem dot. Vygenerovaný soubor může vypadat následovně:

digraph {
graph[dpi=100, rankdir=TP]
node[fontname="Monospace"]
edge[fontname="Monospace"]
 
node5972[label="queue"]
node5973[label="Kafka"]
node5974[label="backend"]
node5975[label="notificator"]
node5976[label="storage"]
node5977[label="service"]
node5978[label="proxy"]
node5979[label="db writer"]
node5980[label="frontend"]
node5981[label="ggregator"]
node5972 -> node5974[label=""]
node5973 -> node5975[label=""]
node5973 -> node5979[label=""]
node5974 -> node5980[label=""]
node5975 -> node5972[label=""]
node5976 -> node5981[label=""]
node5977 -> node5978[label=""]
node5978 -> node5974[label=""]
node5979 -> node5976[label=""]
node5981 -> node5978[label=""]
}

Vraťme se však k zobrazení grafů. Definice grafu je tvořena mapou, v níž klíče jsou tvořeny názvy uzlů a hodnoty vektory, taktéž se jmény uzlů. Každý prvek v mapě tedy představuje jednu či více hran mezi uzly:

(def g {"Algol" ["K&R C"],
        "K&R C" ["ANSI C"]})

Vytvoříme soubor s prvním grafem:

; ulozeni grafu bez popisku uzlu
(viz/save-graph (keys g) g :filename "g01.png")

Obrázek 12: Neúplný graf vytvořený prvním příkladem.

Tento graf neobsahuje jména uzlů, protože jsme žádným způsobem nespecifikovali, jak se mají vytvořit. Pro specifikaci funkce, která pro daný uzel vytvoří i jeho jméno (popř. další atributy – opět reprezentované v mapě) se použije nepovinný parametr :node->descriptor:

; ulozeni grafu i s popisky uzlu
(viz/save-graph (keys g) g :filename "g02.png"
                           :node->descriptor (fn [n] {:label n}))

Nyní bude výsledek vypadat následovně:

Obrázek 13: Neúplný graf vytvořený druhým příkladem.

8. Koncové uzly grafu

Aby se korektně zobrazil i koncový uzel grafu (či koncové uzly), je nutné je v grafu specifikovat formou nového prvku. Nestačí tedy, že specifikujeme, že hrana má vést k danému uzlu – uzel musí být v mapě uložen i formou klíče. Vzhledem k tomu, že z takového uzlu nevedou žádné hrany, bude hodnota prvku nil, popř. prázdný vektor nebo seznam:

; graf, kde i uzly existuji ve forme zaznamu
(def g {"Algol" ["K&R C"],
        "K&R C" ["ANSI C"],
        "ANSI C" nil})

Uložení rastrového obrázku s grafem:

; ulozeni grafu i s popisky uzlu
(viz/save-graph (keys g) g :filename "g03.png"
                           :node->descriptor (fn [n] {:label n}))

Obrázek 14: Úplný graf vytvořený třetím příkladem.

9. Složitější graf, v němž mají uzly více potomků

Podívejme se nyní na nepatrně složitější graf, jehož uzly mají několik potomků. Opět nesmíme zapomenout na explicitní zápis koncových uzlů formou prvků s klíčem, ovšem bez hodnoty:

; slozitejsi graf, kde maji uzly vice potomku
(def g {"Algol" ["K&R C", "Pascal", "Modula"],
        "K&R C" ["ANSI C", "C with classes"],
        "C with classes" ["C++"],
        "ANSI C" ["C89"],
        "C89" ["C99"],
        "Pascal" nil,
        "Modula" nil,
        "C++" nil,
        "C99" nil})

Vykreslení grafu:

; ulozeni grafu i s popisky uzlu
(viz/save-graph (keys g) g :filename "g04.png"
                           :node->descriptor (fn [n] {:label n}))

S výsledkem:

Obrázek 15: Úplný graf vytvořený čtvrtým příkladem.

10. Specifikace stylu vykreslení grafu

Nepovinným parametrem :directed? lze specifikovat, zda mají být hrany orientované nebo neorientované:

; zmena stylu vykresleni grafu - bez orientovanych hran
(viz/save-graph (keys g) g :filename "g05.png"
                           :node->descriptor (fn [n] {:label n})
                           :directed? nil)

Obrázek 16: Graf s neorientovanými hranami („bez šipek“).

Zvolit lze i vykreslení hran spíše horizontálním směrem (koncové uzly jsou v tomto případě zobrazeny vpravo):

; zmena stylu vykresleni grafu - horizontalne orientovany
(viz/save-graph (keys g) g :filename "g06.png"
                           :node->descriptor (fn [n] {:label n})
                           :vertical? false)

Výsledek:

Obrázek 17: Graf orientovaný horizontálně.

11. Popisky hran v grafu

Podobně jako je možné specifikovat data či funkci použitou pro přidání jmen uzlů do grafu, můžeme totéž udělat s hranami. Obecně se jméno hrany počítá ve funkci, která akceptuje dva uzly (které hrana spojuje) a výsledkem má být mapa obsahující v klíči :label jméno. To může obsahovat například i HTML entity atd.:

; ulozeni grafu i s popisky uzlu a hran
(viz/save-graph (keys g) g :filename "g07.png"
                           :node->descriptor (fn [n] {:label n})
                           :edge->descriptor (fn [n1 n2] {:label (str n1 "→" n2)}))

Výsledek:

Obrázek 18: Graf vykreslený předchozím příkladem.

Pokud vám nevyhovuje použití anonymních funkcí (lambd), samozřejmě je možné použít i běžné pojmenované funkce:

(defn make-node-descriptor
  [n]
  {:label n})
 
(defn make-edge-descriptor
  [n1 n2]
  {:label (str n1 "→" n2)})
 
; dtto ovsem bez pouziti anonymnich funkci
; ulozeni grafu i s popisky uzlu a hran
(viz/save-graph (keys g) g :filename "g08.png"
                           :node->descriptor make-node-descriptor
                           :edge->descriptor make-edge-descriptor)

Obrázek 19: Graf vykreslený předchozím příkladem.

12. Modifikace barvy uzlu, popř. stylu vykreslení

U uzlů je možné specifikovat různé styly vykreslení. Mezi měnitelný styl patří barva okraje, popř. barva pozadí. Podívejme se nyní na poněkud umělý příklad, v němž se barva uzlu mění programově na základě jeho jména (ovšem v praxi lze samozřejmě použít složitější funkci, popř. konstantní data):

(defn make-node-descriptor-2
  [n]
  {:label n
   :color (if (= n "ANSI C") "darkgreen" "#000000")})
 
; styly vykresleni uzlu
(viz/save-graph (keys g) g :filename "g09.png"
                           :node->descriptor make-node-descriptor-2
                           :edge->descriptor make-edge-descriptor)

Nyní bude barva uzlu „ANSI C“ odlišná od ostatních uzlů:

Obrázek 20: Graf vykreslený předchozím příkladem.

13. Modifikace stylu vykreslení hran

Nic nám nebrání ani ve změně barvy hran. V dalším příkladu se rozlišuje mezi třemi typy hran – hrana z uzlu „ANSI C“, hrana do uzlu „ANSI C“ a ostatní hrany:

(defn make-edge-descriptor-2
  [n1 n2]
  (let [color (cond (= n1 "ANSI C") "red"
                    (= n2 "ANSI C") "blue"
                    :else "black")]
    {:label (str n1 "→" n2)
     :color color}))

Vykreslení grafu s využitím výše definované funkce make-edge-descriptor-2:

; styly vykresleni hran
(viz/save-graph (keys g) g :filename "g10.png"
                           :node->descriptor make-node-descriptor-2
                           :edge->descriptor make-edge-descriptor-2)

Obrázek 21: Graf vykreslený předchozím příkladem.

Poznámka: v příkladu je použito makro cond, které bylo popsáno v tomto článku.

14. Shlukování uzlů (clustering)

Knihovna Rhizome podporuje shlukování uzlů, tj. provádět takzvaný clustering. V následujícím demonstračním příkladu si graf pro clustering pouze připravíme. Vzhledem k tomu, že funkce, která každému uzlu přiřadí cluster, je nastavená na identity, bude každý uzel umístěn ve svém vlastním clusteru:

; clustering
(viz/save-graph (keys g) g :filename "g11.png"
                           :node->descriptor make-node-descriptor
                           :edge->descriptor make-edge-descriptor
                           :node->cluster identity)

S výsledkem:

Obrázek 22: Graf vykreslený předchozím demonstračním příkladem.

Poznámka: funkce identity je definována v základní knihovně jazyka Clojure:
user=> (doc identity)
-------------------------
clojure.core/identity
([x])
  Returns its argument.

15. Konkrétní specifikace shlukovaných uzlů

Jednou z možností specifikace, které uzly mají být sdruženy, je použití nepovinné volby :cluster->parent. Té se předává mapa se specifikací vztahu mezi dvojicí uzlů, například „ANSI C a C89 jsou sdruženy“ a současně „C89 a C99“ jsou sdruženy. Výsledkem je následující hierarchie:

; clustering
(viz/save-graph (keys g) g :filename "g12.png"
                           :node->descriptor make-node-descriptor
                           :edge->descriptor make-edge-descriptor
                           :node->cluster identity
                           :cluster->parent {"ANSI C" "C89"
                                             "C89" "C99"
                                             })

S výsledkem:

Obrázek 23: Graf vykreslený předchozím demonstračním příkladem.

Poněkud odlišná specifikace vztahů mezi uzly:

; clustering
(viz/save-graph (keys g) g :filename "g13.png"
                           :node->descriptor make-node-descriptor
                           :edge->descriptor make-edge-descriptor
                           :node->cluster identity
                           :cluster->parent {"ANSI C" "C89"
                                             "C with classes" "C++"
                                             "Pascal" "Modula"})

S výsledkem:

Obrázek 24: Graf vykreslený předchozím demonstračním příkladem.

V praxi může být užitečnější použití nepovinného parametru :node->cluster, v němž jsou jména uzlů namapována na jména clusterů (a tato jména mohou být klidně totožná se jménem uzlu). Opět si pochopitelně ukážeme jednoduchý demonstrační příklad se třemi clustery pojmenovanými „C“, „C++“ a „Wirth“:

; clustering
(viz/save-graph (keys g) g :filename "g14.png"
                           :node->descriptor make-node-descriptor
                           :edge->descriptor make-edge-descriptor
                           :node->cluster {"ANSI C" "C"
                                           "C89" "C"
                                           "C99" "C"
                                           "C with classes" "C++"
                                           "C++" "C++"
                                           "Pascal" "Wirth"
                                           "Modula" "Wirth"})

Nyní bude graf vypadat odlišně:

Obrázek 25: Graf vykreslený předchozím demonstračním příkladem.

16. Kombinace předchozích příkladů

Většinu voleb a možností je možné různým způsobem kombinovat (nevylučují se), což je ostatně ukázáno v dnešním posledním úryvku zdrojového kódu:

; slozitejsi graf, kde maji uzly vice potomku
(def g {"Algol" ["K&R C", "Pascal", "Modula"],
        "K&R C" ["ANSI C", "C with classes"],
        "C with classes" ["C++"],
        "ANSI C" ["C89"],
        "C89" ["C99"],
        "Pascal" nil,
        "Modula" nil,
        "C++" nil,
        "C99" nil})
 
(defn make-node-descriptor-2
  [n]
  {:label n
   :color (if (= n "ANSI C") "darkgreen" "#000000")})
 
(defn make-edge-descriptor-2
  [n1 n2]
  (let [color (cond (= n1 "ANSI C") "red"
                    (= n2 "ANSI C") "blue"
                    :else "black")]
    {:label (str n1 "→" n2)
     :color color}))
 
; kombinace predchozich prikladu
(viz/save-graph (keys g) g :filename "g15.png"
                           :node->descriptor make-node-descriptor-2
                           :edge->descriptor make-edge-descriptor-2
                           :node->cluster {"ANSI C" "C"
                                           "C89" "C"
                                           "C99" "C"
                                           "C with classes" "C++"
                                           "C++" "C++"
                                           "Pascal" "Wirth"
                                           "Modula" "Wirth"}
                           :directed? nil)

Obrázek 26: Graf vykreslený předchozím demonstračním příkladem s kombinací více možností ovlivňujících tvar i styl vykreslení grafu.

Na závěr si necháme vygenerovat soubor typu .dot s popisem grafu:

(spit "test.dot"
      (dot/graph->dot (keys g) g :node->descriptor make-node-descriptor-2
                      :edge->descriptor make-edge-descriptor-2
                      :node->cluster {"ANSI C" "C"
                                      "C89" "C"
                                      "C99" "C"
                                      "C with classes" "C++"
                                      "C++" "C++"
                                      "Pascal" "Wirth"
                                      "Modula" "Wirth"}
                      :directed? nil))

S tímto výsledkem:

MIF21_Dolejsova

graph {
graph[dpi=100, rankdir=TP]
node[fontname="Monospace"]
edge[fontname="Monospace"]
 
node5972[label="K&R C", color="#000000"]
node5973[label="Algol", color="#000000"]
subgraph cluster5974 {
graph[dpi=100, fontname="Monospace", rankdir=TP]
node[fontname="Monospace"]
edge[fontname="Monospace"]
 
node5975[label="C89", color="#000000"]
node5976[label="ANSI C", color="darkgreen"]
node5977[label="C99", color="#000000"]
}
 
subgraph cluster5978 {
graph[dpi=100, fontname="Monospace", rankdir=TP]
node[fontname="Monospace"]
edge[fontname="Monospace"]
 
node5979[label="Pascal", color="#000000"]
node5980[label="Modula", color="#000000"]
}
 
subgraph cluster5981 {
graph[dpi=100, fontname="Monospace", rankdir=TP]
node[fontname="Monospace"]
edge[fontname="Monospace"]
 
node5982[label="C++", color="#000000"]
node5983[label="C with classes", color="#000000"]
}
 
node5972 -- node5976[label="K&R C→ANSI C", color="blue"]
node5972 -- node5983[label="K&R C→C with classes", color="black"]
node5975 -- node5977[label="C89→C99", color="black"]
node5973 -- node5972[label="Algol→K&R C", color="black"]
node5973 -- node5979[label="Algol→Pascal", color="black"]
node5973 -- node5980[label="Algol→Modula", color="black"]
node5976 -- node5975[label="ANSI C→C89", color="red"]
node5983 -- node5982[label="C with classes→C++", color="black"]

17. Repositář s demonstračním příkladem

Projekt, v němž je definována závislost na knihovně s nástrojem Rhizome (a nepřímo na Graphviz), byl uložen do Git repositáře, který je dostupný na adrese https://github.com/tisnik/clojure-examples/. Konkrétně se jedná o projekt nazvaný rhizome-1 (https://github.com/tisnik/clojure-examples/tree/master/rhizome-1).

Poznámka: všechny příklady spouštěné v REPLu jsou k dispozici zde.

18. Odkazy na články s tématem programové tvorby grafů a diagramů

V této kapitole jsou uvedeny odkazy na předchozí články, v nichž jsme se zabývali tvorbou různých typů grafů a diagramů – a to v naprosté většině případů s využitím nějakého doménově specifického jazyka neboli DSL (Domain Specific Language):

  1. Nástroje pro tvorbu UML diagramů
    https://www.root.cz/clanky/nastroje-pro-tvorbu-uml-diagramu/
  2. Nástroje pro tvorbu UML diagramů z příkazové řádky
    https://www.root.cz/clanky/nastroje-pro-tvorbu-uml-diagramu-z-prikazove-radky/
  3. Nástroje pro tvorbu UML diagramů z příkazové řádky (II)
    https://www.root.cz/clanky/nastroje-pro-tvorbu-uml-diagramu-z-prikazove-radky-ii/
  4. Nástroje pro tvorbu grafů a diagramů z příkazové řádky
    https://www.root.cz/clanky/nastroje-pro-tvorbu-grafu-a-diagramu-z-prikazove-radky/
  5. Sledování správy paměti v Pythonu s využitím nástroje objgraph
    https://www.root.cz/clanky/sledovani-spravy-pameti-v-pythonu-s-vyuzitim-nastroje-objgraph/

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 azyce 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/
  64. Interpret programovacího jazyka Clojure integrovaný do Jupyter Notebooku
    https://www.root.cz/clanky/interpret-programovaciho-jazyka-clojure-integrovany-do-jupyter-notebooku/
  65. Babashka: interpret Clojure určený pro rychlé spouštění utilit z příkazového řádku
    https://www.root.cz/clanky/babashka-interpret-clojure-urceny-pro-rychle-spousteni-utilit-z-prikazoveho-radku/
  66. Pokročilý streaming založený na Apache Kafce, jazyku Clojure a knihovně Jackdaw
    https://www.root.cz/clanky/pokrocily-streaming-zalozeny-na-apache-kafce-jazyku-clojure-a-knihovne-jackdaw/
  67. Pokročilý streaming založený na Apache Kafce, jazyku Clojure a knihovně Jackdaw (2. část)
    https://www.root.cz/clanky/pokrocily-streaming-zalozeny-na-apache-kafce-jazyku-clojure-a-knihovne-jackdaw-2-cast/
  68. Pokročilý streaming založený na projektu Apache Kafka, jazyku Clojure a knihovně Jackdaw (streamy a kolony)
    https://www.root.cz/clanky/pokrocily-streaming-zalozeny-na-projektu-apache-kafka-jazyku-clojure-a-knihovne-jackdaw-streamy-a-kolony/
  69. Řídicí struktury využitelné v programovacím jazyku Clojure
    https://www.root.cz/clanky/ridici-struktury-vyuzitelne-v-programovacim-jazyku-clojure/
  70. Řídicí struktury využitelné v programovacím jazyku Clojure (dokončení)
    https://www.root.cz/clanky/ridici-struktury-vyuzitelne-v-programovacim-jazyku-clojure-dokonceni/
  71. Formát EDN: extensible data notation
    https://www.root.cz/clanky/format-edn-extensible-data-notation/
  72. Formát EDN: extensible data notation (dokončení)
    https://www.root.cz/clanky/format-edn-extensible-data-notation-dokonceni/
  73. Čtyři různé podoby datové struktury map v programovacím jazyku Clojure
    https://www.root.cz/clanky/ctyri-ruzne-podoby-datove-struktury-map-v-programovacim-jazyku-clojure/

20. Odkazy na Internetu

  1. Rhizome
    https://github.com/ztellman/rhizome
  2. Napkin
    https://github.com/pinetr2e/napkin
  3. Swagger to UML
    https://github.com/nlohman­n/swagger_to_uml
  4. pydiagrams
    https://github.com/billin­gtonm/pydiagrams
  5. graphviz(3) – Linux man page
    https://linux.die.net/man/3/graphviz
  6. dot(1) – Linux man page
    https://linux.die.net/man/1/dot
  7. neato(1) – Linux man page
    https://linux.die.net/man/1/neato
  8. twopi(1) – Linux man page
    https://linux.die.net/man/1/twopi
  9. circo(1) – Linux man page
    https://linux.die.net/man/1/circo
  10. fdp(1) – Linux man page
    https://linux.die.net/man/1/fdp
  11. sfdp(1) – Linux man page
    https://linux.die.net/man/1/sfdp
  12. Plain-text diagrams take shape in Asciidoctor!
    http://asciidoctor.org/new­s/2014/02/18/plain-text-diagrams-in-asciidoctor/
  13. Graphviz – Graph Visualization Software
    http://www.graphviz.org/
  14. graphviz (Manual Page)
    http://www.root.cz/man/7/graphviz/
  15. dot (Manual page)
    http://www.root.cz/man/1/dot/
  16. dot (Manual v PDF)
    https://graphviz.org/pdf/dot.1.pdf
  17. Ditaa home page
    http://ditaa.sourceforge.net/
  18. Ditaa introduction
    http://ditaa.sourceforge.net/#intro
  19. Ditaa usage
    http://ditaa.sourceforge.net/#usage
  20. Node, Edge and Graph Attributes
    http://www.graphviz.org/doc/in­fo/attrs.html
  21. Graphviz (Wikipedia)
    http://en.wikipedia.org/wiki/Graphviz

Autor článku

Pavel Tišnovský vystudoval VUT FIT a v současné době pracuje ve společnosti Red Hat, kde vyvíjí nástroje pro OpenShift.io.