Obsah
1. Babashka – interpret Clojure určený pro rychlé spouštění utilit z příkazového řádku
2. Instalace Babashky a první otestování základní funkcionality
3. Jak rychlé je spouštění skriptů, informace o spotřebě dalších prostředků
4. Použití shebangu pro spuštění skriptů
5. Implicitní výstup ze skriptu
6. Hodnota navázaná na symbol *input*
9. Zpracování argumentů předaných na příkazové řádce
10. Základní práce s HTTP/HTTPS, popř. s REST API
11. Zpracování dat vracených ve formátu JSON
12. Ukázka síly jazyka Clojure – threading makro
13. Využití dalších vlastností Clojure – výpočty s neomezeným rozsahem hodnot
14. Datový typ double a počítání se zlomky
15. Výpočty ve více vláknech – map versus pmap
16. Porovnání rychlosti výpočtů Babashky s implementací Clojure pro JVM
17. Zobrazení nápovědy přímo v interaktivní smyčce REPL
18. Repositář s demonstračními příklady
19. Odkazy na předchozí části seriálu o programovacím jazyku Clojure
1. Babashka – interpret Clojure určený pro rychlé spouštění utilit z příkazového řádku
S programovacím jazykem Clojure jsme se již na stránkách Roota mnohokrát setkali. Popisovali jsme si jak interpret a překladač běžící nad JVM, tak i (i když ve stručnosti) ClojureScript, tedy transpřekladač z Clojure do JavaScriptu. Tyto dvě implementace Clojure mají jednu společnou (a dosti nepěknou) vlastnost – spuštění skriptů, popř. spuštění interaktivní smyčky REPL je dosti zdlouhavé a i na nových výkonných počítačích může trvat několik sekund. To sice nevadí při delší práci (představme si programátora, který má REPL otevřený celý pracovní den), ovšem i z tohoto důvodu se Clojure nepoužívá pro psaní skriptů, které mají být rychle spouštěny z příkazové řádky. Taktéž delší inicializace REPLu není příliš dobrým „reklamním materiálem“ tohoto jazyka, i když výsledné aplikace přeložené do bajtkódu a JITované v JVM mají většinou velmi dobrou výkonnost.
Řešení tohoto problému spočívá v použití nástroje nazvaného Babashka. Jedná se o interpret Clojure naprogramovaný taktéž v Clojure (což není ve světě LISPu nic neobvyklého). Ovšem tento interpret je přeložen do nativního kódu s využitím GraalVM, takže výsledkem je spustitelný (binární) soubor. Důležité je, že je spustitelný prakticky okamžitě, takže REPL či interpretace skriptů spouštěných z příkazové řádky či z BASHe je již velmi dobře možná. Navíc je Babashka dodávána „včetně baterií“, což znamená, že holý interpret je doplněn mnoha užitečnými balíčky – viz též sedmou kapitolu.
2. Instalace Babashky a první otestování základní funkcionality
Instalace Babashky je popsána na stránce https://github.com/borkdude/babashka#quickstart. Nejprve je nutné stáhnout krátký instalační skript:
$ curl -s https://raw.githubusercontent.com/borkdude/babashka/master/install -o install-babashka
Nyní je vhodné si prohlédnout, co skript dělá, protože ho budeme spouštět s právy roota:
$ chmod +x install-babashka && sudo ./install-babashka
Výsledný spustitelný soubor by měl být dostupný v podadresáři /usr/local/bin, o čemž se můžeme snadno přesvědčit:
$ ls -l /usr/local/bin
Stažený a nainstalovaný nástroj Babashka je zobrazen na prvním místě:
total 184948 -rwxr-xr-x. 1 root root 67231896 Jun 27 13:39 bb -rwxr-xr-x. 1 root root 218 Oct 9 2019 flake8 -rwxr-xr-x. 1 root root 384 Oct 9 2019 futurize -rwxr-xr-x. 1 root root 120350344 Sep 27 2019 oc -rwxr-xr-x. 1 root root 388 Oct 9 2019 pasteurize -rwxr-xr-x. 1 root root 216 Oct 9 2019 pycodestyle -rwxr-xr-x. 1 root root 215 Oct 8 2019 pyflakes -rwxr-xr-x. 1 root root 208 Oct 9 2019 radon -rwxr-xr-x. 1 root root 213 Oct 3 2019 virtualenv -rwxr-xr-x. 1 root root 215 Oct 8 2019 vulture -rwxr-xr-x. 1 root root 1750603 Mar 23 21:15 youtube-dl
Kontrola, zda je interpret skutečně spustitelný:
$ bb --help Babashka v0.1.3 Options must appear in the order of groups mentioned below. Help: --help, -h or -? Print this help text. --version Print the current version of babashka. --describe Print an EDN map with information about this version of babashka. In- and output flags: -i Bind *input* to a lazy seq of lines from stdin. -I Bind *input* to a lazy seq of EDN values from stdin. -o Write lines to stdout. -O Write EDN values to stdout. --stream Stream over lines or EDN values from stdin. Combined with -i or -I *input* becomes a single value per iteration. Uberscript: --uberscript <file> Collect preloads, -e, -f and -m and all required namespaces from the classpath into a single executable file. Evaluation: -e, --eval <expr> Evaluate an expression. -f, --file <path> Evaluate a file. -cp, --classpath Classpath to use. -m, --main <ns> Call the -main function from namespace with args. --verbose Print entire stacktrace in case of exception. REPL: --repl Start REPL. Use rlwrap for history. --socket-repl Start socket REPL. Specify port (e.g. 1666) or host and port separated by colon (e.g. 127.0.0.1:1666). --nrepl-server Start nREPL server. Specify port (e.g. 1667) or host and port separated by colon (e.g. 127.0.0.1:1667). If neither -e, -f, or --socket-repl are specified, then the first argument that is not parsed as a option is treated as a file if it exists, or as an expression otherwise. Everything after that is bound to *command-line-args*. Use -- to separate script command lin args from bb command line args.
Můžeme si vyzkoušet i interaktivní smyčku REPL:
$ bb Babashka v0.1.3 REPL. Use :repl/quit or :repl/exit to quit the REPL. Clojure rocks, Bash reaches. user=>
Poslední jednoduchý test – pokusíme se interpretovat následující skript:
(println "Hello" "world")
Spuštění interpretru:
$ bb 01_hello.clj Hello world
3. Jak rychlé je spouštění skriptů, informace o spotřebě dalších prostředků
Vyzkoušejme si nyní, jak rychle je spuštěn a dokončen skript „Hello world“:
$ time bb 01_hello.clj Hello world real 0m0.030s user 0m0.010s sys 0m0.017s
To není špatné; navíc se bb uložil do bufferu, takže druhé spuštění bude ještě rychlejší:
$ time bb 01_hello.clj Hello world real 0m0.014s user 0m0.003s sys 0m0.011s
Výsledek: spuštění interpretru je prakticky okamžité; žádné prodlevy, které známe z použití Leiningenu, zde nejsou patrné!
Ještě se podívejme, jaké vlastnosti má spuštěný proces z interpretrem. Vytvoříme nový skript, který po svém spuštění čeká na stisk klávesy Enter:
(println "Press Enter to continue...") (read-line)
Skript spustíme v interpretru:
$ bb 02_wait_for_user.clj Press Enter to continue...
Nalezneme PID procesu s interpretrem (je to první PID):
$ ps ax |grep 02_wait_for_user 14649 pts/1 Sl+ 0:00 bb 02_wait_for_user.clj 14758 pts/2 S+ 0:00 grep 02_wait_for_user
A podíváme se na podrobnější informace o tomto procesu, které jsou poskytované jádrem operačního systému:
$ cat /proc/14649/status
Name: bb Umask: 0002 State: S (sleeping) Tgid: 14649 Ngid: 0 Pid: 14649 PPid: 29490 TracerPid: 0 Uid: 1000 1000 1000 1000 Gid: 1000 1000 1000 1000 FDSize: 256 Groups: 39 982 1000 1001 NStgid: 14649 NSpid: 14649 NSpgid: 14649 NSsid: 29490 VmPeak: 227912 kB VmSize: 163400 kB VmLck: 0 kB VmPin: 0 kB VmHWM: 38576 kB VmRSS: 38576 kB RssAnon: 7012 kB RssFile: 31564 kB RssShmem: 0 kB VmData: 23776 kB VmStk: 132 kB VmExe: 57612 kB VmLib: 0 kB VmPTE: 200 kB VmSwap: 0 kB HugetlbPages: 0 kB CoreDumping: 0 Threads: 2 SigQ: 0/61760 SigPnd: 0000000000000000 ShdPnd: 0000000000000000 SigBlk: 0000000000000000 SigIgn: 0000000000000000 SigCgt: 2000000180001402 CapInh: 0000000000000000 CapPrm: 0000000000000000 CapEff: 0000000000000000 CapBnd: 0000003fffffffff CapAmb: 0000000000000000 NoNewPrivs: 0 Seccomp: 0 Speculation_Store_Bypass: thread vulnerable Cpus_allowed: ff Cpus_allowed_list: 0-7 Mems_allowed: 00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000001 Mems_allowed_list: 0 voluntary_ctxt_switches: 9 nonvoluntary_ctxt_switches: 1
4. Použití shebangu pro spuštění skriptů
Babashka je primárně určena pro tvorbu pomocných skriptů, které jsou příliš složité na to, aby je programátor či administrátor psal v BASHi. Takové skripty (nebo řekněme nástroje či filtry) se většinou nespouští zadáním názvu interpretru, ale přímo. I to je možné, protože můžeme na začátku skriptů použít známý shebang, který může obsahovat přímo cestu k interpretru:
#!/usr/local/bin/bb (println "Hello" "world")
Nastavíme atribut x u skriptu:
$ chmod u+x 03_shebang.clj
A můžeme ho přímo spustit:
$ ./03_shebang.clj Hello world
Čistější je spustit skript v modifikovaném prostředí, tudíž shebang příslušným způsobem změníme:
#!/usr/bin/env bb (println "Hello" "world")
Použití je naprosto stejné, jako v předchozím příkladu:
$ chmod u+x 04_shebang.clj $ ./04_shebang.clj Hello world
5. Implicitní výstup ze skriptu
Ve skutečnosti lze „Hello world“ vypsat ještě kratším skriptem, než jaký byl uveden v předchozích kapitolách:
#!/usr/bin/env bb "Hello world"
Tento skript pracuje tak, že poslední hodnotou (tedy řetězec) pošle na svůj standardní výstup.
Můžeme ovšem generovat i víceřádkový výstup, zde konkrétně v kombinaci se standardní funkcí println:
#!/usr/bin/env bb (doseq [i (range 1 10)] (println i)) "Hello world"
S výsledkem:
1 2 3 4 5 6 7 8 9 "Hello world"
6. Hodnota navázaná na symbol *input*
Pokud se skript interpretovaný Babashkou spustí s přepínačem -i, bude standardní vstup (resp. přesněji řečeno jeho obsah) převeden na sekvenci a navázán na symbol *input* (symbol můžeme v tomto případě považovat za pojmenovanou konstantu, i když to není přesné). Obsah sekvence si tedy můžeme snadno vypsat:
(println *input*)
Příklad použití – na standardním vstupu bude seznam souborů, který bude převeden na sekvenci:
$ ls -1 | bb -i 07_print_input.clj (01_hello.clj 02_wait_for_user.clj 03_shebang.clj 04_shebang.clj 05_implicit_output.clj 06_multiline_output.clj 07_print_input.clj 08_input_type.clj 09_sort_input.clj 10_for_each_input.clj 11_to_json.clj 12_cli_arguments.clj 13_cli_arguments.clj 14_http_get_to_text.clj 15_http_get_processing.clj 16_http_get_processing.clj 17_http_get_processing_args.clj 18_factorial_overflow.clj 19_factorial_bigint.clj 20_pi_computation_double.clj 21_pi_computation_rational.clj 22_sequential_map.clj 23_parallel_map.clj 24_parallel_map_clojure.clj license.clj README.md)
Snadno se můžeme přesvědčit, jakého typu vlastně vstup je:
(println (type *input*))
S výsledky:
$ ls -1 | bb -i 08_input_type.clj clojure.lang.Cons
Se vstupem lze provádět různé operace, například ho setřídit a posléze vypsat (zde je použito threading makro):
(-> *input* sort println)
Otestování:
$ ls -1 | bb -i 09_sort_input.clj (01_hello.clj 02_wait_for_user.clj 03_shebang.clj 04_shebang.clj 05_implicit_output.clj 06_multiline_output.clj 07_print_input.clj 08_input_type.clj 09_sort_input.clj 10_for_each_input.clj 11_to_json.clj 12_cli_arguments.clj 13_cli_arguments.clj 14_http_get_to_text.clj 15_http_get_processing.clj 16_http_get_processing.clj 17_http_get_processing_args.clj 18_factorial_overflow.clj 19_factorial_bigint.clj 20_pi_computation_double.clj 21_pi_computation_rational.clj 22_sequential_map.clj 23_parallel_map.clj 24_parallel_map_clojure.clj README.md license.clj)
Taktéž můžeme sekvenci zpracovat prvek po prvku:
(doseq [i *input*] (println i))
Nyní bude výsledek odlišný:
$ ls -1 | bb -i 10_for_each_input.clj 01_hello.clj 02_wait_for_user.clj 03_shebang.clj 04_shebang.clj 05_implicit_output.clj 06_multiline_output.clj 07_print_input.clj 08_input_type.clj 09_sort_input.clj 10_for_each_input.clj 11_to_json.clj 12_cli_arguments.clj 13_cli_arguments.clj 14_http_get_to_text.clj 15_http_get_processing.clj 16_http_get_processing.clj 17_http_get_processing_args.clj 18_factorial_overflow.clj 19_factorial_bigint.clj 20_pi_computation_double.clj 21_pi_computation_rational.clj 22_sequential_map.clj 23_parallel_map.clj 24_parallel_map_clojure.clj license.clj README.md
7. Dostupné balíčky
Babashka ovšem není „pouze“ interpretrem programovacího jazyka Clojure bez dalších podpůrných funkcí. Ve skutečnosti obsahuje Babashka relativně velké množství balíčků, které lze přímo použít (což do jisté míry vysvětluje i to, proč je spustitelný soubor bb tak velký). Tyto balíčky nebo jejich části by měly uspokojit velkou část programátorů při vytváření nástrojů spouštěných z příkazové řádky. Chybí prakticky jen plnohodnotný HTTP klient. Ostatně se můžete sami přesvědčit, které balíčky jsou dostupné, popř. které funkce a makra z nich je možné přímo použít, a to bez nutnosti importu externích balíčků nebo Javovských knihoven:
# | Plné jméno | Alias | Podporované funkce |
---|---|---|---|
1 | clojure.string | str | všechny |
2 | clojure.set | set | všechny |
3 | clojure.edn | edn | read-string |
4 | clojure.java.shell | shell | všechny |
5 | clojure.java.io | io | as-relative-path, as-url, copy, delete-file, file, input-stream, make-parents, output-stream, reader, resource, writer |
6 | clojure.main | clojure.main | repl |
7 | clojure.core.async | async | všechny |
8 | clojure.stacktrace | × | všechny |
9 | clojure.test | × | všechny |
10 | clojure.pprint | × | pprint |
11 | clojure.zip | × | všechny |
12 | clojure.tools.cli | tools.cli | všechny |
13 | clojure.data.csv | csv | všechny |
14 | clojure.data.xml | xml | všechny |
15 | cheshire.core | json | všechny |
16 | cognitect.transit | transit | všechny |
17 | clj-yaml.core | yaml | všechny |
18 | bencode.core | bencode | read-bencode, write-bencode |
8. Výstup do JSONu
Některé balíčky a funkce či makra v nich definované si můžeme odzkoušet velmi snadno. V následujícím jednořádkovém demonstračním příkladu se hodnota na vstupu navázaná na symbol *input* převede do formátu JSON a vytiskne na standardní výstup (takže je JSON dostupný pro zpracování dalšími nástroji):
(println (json/encode *input*))
Vzhledem k tomu, že na vstupu je sekvence textových řádků, bude výstupní JSON obsahovat pole s řetězci, přičemž každý řetězec odpovídá jednomu vstupnímu řádku:
$ ls -1 | bb -i 11_to_json.clj ["01_hello.clj","02_wait_for_user.clj","03_shebang.clj","04_shebang.clj", "05_implicit_output.clj","06_multiline_output.clj","07_print_input.clj", "08_input_type.clj","09_sort_input.clj","10_for_each_input.clj","11_to_json.clj", "12_cli_arguments.clj","13_cli_arguments.clj","14_http_get_to_text.clj", "15_http_get_processing.clj","16_http_get_processing.clj","17_http_get_processing_args.clj", "18_factorial_overflow.clj","19_factorial_bigint.clj","20_pi_computation_double.clj", "21_pi_computation_rational.clj","22_sequential_map.clj","23_parallel_map.clj", "24_parallel_map_clojure.clj","license.clj","README.md"]
9. Zpracování argumentů předaných na příkazové řádce
Mnoho skriptů spouštěných z příkazové řádky (ať již jsou naprogramovány v jakémkoli jazyce) musí nějakým způsobem zpracovávat argumenty zadané uživatelem či jiným skriptem při volání. Pro tento účel se v programovacím jazyku Clojure používá balíček nazvaný clojure.tools.cli, který je dostupný i v nástroji Babashka. Bližší popis možností tohoto balíčku naleznete na stránce https://github.com/clojure/tools.cli. My si nyní v krátkosti ukážeme způsob použití pro skript, který má akceptovat argumenty -v, -h a argument s parametrem -p. Argumenty příkazové řádky se definují strukturou typu „vektor vektorů“ a zpracovávají funkcí parse-opts vracející informace o předaných parametrech:
(require '[clojure.pprint :as pprint]) (require '[clojure.tools.cli :refer [parse-opts]]) (def command-line-options [["-v" "--verbose" "Verbosity level" :id :verbosity :default 0 :update-fn inc] ["-p" "--port PORT" "Port number" :default 80 :parse-fn #(Integer/parseInt %) :validate [#(< 0 % 0x10000) "Must be a number between 0 and 65536"]] ["-h" "--help"]]) (pprint/pprint (parse-opts *command-line-args* command-line-options))
Datová struktura vracená funkcí parse-opts je v čitelné podobě vypsána na standardní výstup, takže si můžeme otestovat, jak bude skript reagovat na různé argumenty, popř. na jejich absenci.
Spuštění bez argumentů:
$ bb 12_cli_arguments.clj {:options {:verbosity 0, :port 80}, :arguments [], :summary " -v, --verbose Verbosity level\n -p, --port PORT 80 Port number\n -h, --help", :errors nil}
Specifikace jediného argumentu:
$ bb 12_cli_arguments.clj -h {:options {:verbosity 0, :port 80, :help true}, :arguments [], :summary " -v, --verbose Verbosity level\n -p, --port PORT 80 Port number\n -h, --help", :errors nil}
Specifikace dvou argumentů, jeden má i hodnotu:
$ bb 12_cli_arguments.clj -h -p 42 {:options {:verbosity 0, :port 42, :help true}, :arguments [], :summary " -v, --verbose Verbosity level\n -p, --port PORT 80 Port number\n -h, --help", :errors nil}
Použití delšího jména argumentu:
$ bb 12_cli_arguments.clj -h -p 42 --verbose {:options {:verbosity 1, :port 42, :help true}, :arguments [], :summary " -v, --verbose Verbosity level\n -p, --port PORT 80 Port number\n -h, --help", :errors nil}
Předání neznámých argumentů:
$ bb 12_cli_arguments.clj --foobar -x {:options {:verbosity 0, :port 80}, :arguments [], :summary " -v, --verbose Verbosity level\n -p, --port PORT 80 Port number\n -h, --help", :errors ["Unknown option: \"--foobar\"" "Unknown option: \"-x\""]}
Podobně lze tentýž přístup použít i ve chvíli, kdy je skript spustitelný a začíná shebangem:
#!/usr/bin/env bb (require '[clojure.pprint :as pprint]) (require '[clojure.tools.cli :refer [parse-opts]]) (def command-line-options [["-v" "--verbose" "Verbosity level" :id :verbosity :default 0 :update-fn inc] ["-p" "--port PORT" "Port number" :default 80 :parse-fn #(Integer/parseInt %) :validate [#(< 0 % 0x10000) "Must be a number between 0 and 65536"]] ["-h" "--help"]]) (pprint/pprint (parse-opts *command-line-args* command-line-options))
Opět si skript několikrát spustíme.
Použití jediného argumentu v dlouhé podobě:
$ ./13_cli_arguments.clj --help {:options {:verbosity 0, :port 80, :help true}, :arguments [], :summary " -v, --verbose Verbosity level\n -p, --port PORT 80 Port number\n -h, --help", :errors nil}
Předání více argumentů v dlouhé podobě:
$ ./13_cli_arguments.clj --help --port 42 --verbose {:options {:verbosity 1, :port 42, :help true}, :arguments [], :summary " -v, --verbose Verbosity level\n -p, --port PORT 80 Port number\n -h, --help", :errors nil}
Předání více argumentů v krátké podobě:
$ ./13_cli_arguments.clj -h -p 42 -v {:options {:verbosity 1, :port 42, :help true}, :arguments [], :summary " -v, --verbose Verbosity level\n -p, --port PORT 80 Port number\n -h, --help", :errors nil}
Předání neznámých argumentů:
$ ./13_cli_arguments.clj -h -p 42 --foobar {:options {:verbosity 0, :port 42, :help true}, :arguments [], :summary " -v, --verbose Verbosity level\n -p, --port PORT 80 Port number\n -h, --help", :errors ["Unknown option: \"--foobar\""]}
10. Základní práce s HTTP/HTTPS, popř. s REST API
Velmi užitečná může být práce s HTTP či HTTPS, protože kombinace curl + sed nebo curl + jq nemusí být vždy to nejlepší možné řešení. Podívejme se na skript, který přistoupí na REST API a vypíše tělo odpovědi. Použijeme standardní funkci slurp:
(println (slurp "https://httpbin.org/get"))
Po spuštění tohoto skriptu by se na standardní výstup měl vypsat následující JSON:
{ "args": {}, "headers": { "Accept": "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2", "Host": "httpbin.org", "User-Agent": "Java/11.0.7", "X-Amzn-Trace-Id": "Root=1-5f32df22-ecb57d4c0414da444b2dc6e8" }, "origin": "37.48.9.246", "url": "https://httpbin.org/get" }
11. Zpracování dat vracených ve formátu JSON
Předchozí skript lze samozřejmě nahradit voláním utility curl, ovšem předností Babashky (a vůbec Clojure) je excelentní práce se strukturovanými daty. Další skript opět přistoupí k REST API, ovšem z odpovědi nyní získá pouze některá data, konkrétně obsah hlavičky „User-Agent“. „Ukecaná“ verze skriptu by mohla vypadat následovně:
(let [response (slurp "https://httpbin.org/get") parsed (json/decode response true) headers (:headers parsed) user-agent (:User-Agent headers)] (println user-agent))
Po spuštění by se měl vypsat jediný řádek:
Java/11.0.7
12. Ukázka síly jazyka Clojure – threading makro
Předchozí skript si můžeme přepsat do lepší podoby používající takzvané „threading makro“, které předává návratovou hodnotu nějaké formy jako první parametr další formy. Začíná se řetězcem, který se vyhodnotí sám na sebe. Tento řetězec je předán funkci slurp, která získá tělo odpovědi. To zpracujeme jako vstup v JSONu a následně můžeme přistupovat k jednotlivým atributům zkráceným voláním „get“:
(-> "https://httpbin.org/get" slurp (json/decode true) :headers :User-Agent println)
Výsledek bude shodný s předchozím skriptem:
Java/11.0.7
Další skript je kombinací znalostí, které již máme. Skript akceptuje povinný argument s názvem serveru a z tohoto serveru se pak snaží získat odpověď přes jednoduché REST API. Pro větší zábavu je opět použito threading makro:
#!/usr/bin/env bb (require '[clojure.pprint :as pprint]) (require '[clojure.tools.cli :refer [parse-opts]]) (import (java.net InetAddress)) (def command-line-options [["-H" "--hostname HOST" "Remote host" :default "localhost" :required true ]]) (let [opts (parse-opts *command-line-args* command-line-options) url (-> opts :options :hostname)] (-> (str "https://" url "/get") slurp (json/decode true) pprint/pprint))
Příklad použití bez uvedení argumentu:
$ ./17_http_get_processing_args.clj java.net.ConnectException: Connection refused (Connection refused) [at /home/ptisnovs/src/clojure/clojure-examples/babashka/17_http_get_processing_args.clj, line 18, column 7]
Příklad použití s uvedením argumentu:
$ ./17_http_get_processing_args.clj -H httpbin.org {:args {}, :headers {:Accept "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2", :Host "httpbin.org", :User-Agent "Java/11.0.7", :X-Amzn-Trace-Id "Root=1-5f32e7ed-23b74824f4391614a1e2853a"}, :origin "37.48.9.246", :url "https://httpbin.org/get"}
13. Využití dalších vlastností Clojure – výpočty s neomezeným rozsahem hodnot
Podporovány jsou i další užitečné vlastnosti programovacího jazyka Clojure, například výpočty s neomezeným rozsahem hodnot (což je základní požadavek, který na vysokoúrovňové jazyky mám). Podívejme se na výpočet faktoriálu, který je založen na typu long, resp. přesněji řečeno java.lang.Long:
(defn factorial [n] (if (neg? n) (throw (IllegalArgumentException. "negative numbers are not supported!")) (apply * (range 1 (inc n))))) (defn main [max] (doseq [i (range 0 (inc max))] (println i "! = " (factorial i)))) (main 50)
Tento výpočet pro větší hodnoty n skončí s chybou – a Clojure/Babashka správně tuto chybu detekuje:
$ bb 18_factorial_overflow.clj 0 ! = 1 1 ! = 1 2 ! = 2 3 ! = 6 4 ! = 24 5 ! = 120 6 ! = 720 7 ! = 5040 8 ! = 40320 9 ! = 362880 10 ! = 3628800 11 ! = 39916800 12 ! = 479001600 13 ! = 6227020800 14 ! = 87178291200 15 ! = 1307674368000 16 ! = 20922789888000 17 ! = 355687428096000 18 ! = 6402373705728000 19 ! = 121645100408832000 20 ! = 2432902008176640000 java.lang.ArithmeticException: integer overflow [at /home/ptisnovs/src/clojure/clojure-examples/babashka/18_factorial_overflow.clj, line 5, column 9]
Řešení tohoto problému je snadné – postačuje použít datový typ bigdec, tj. typ, který může obsahovat celočíselnou hodnotu o prakticky libovolném rozsahu (omezeni jsme dostupnou pamětí a výpočetním výkonem):
(defn factorial [n] (if (neg? n) (throw (IllegalArgumentException. "negative numbers are not supported!")) (apply * (range 1N (inc n))))) (defn main [max] (doseq [i (range 0 (inc max))] (println i "! = " (factorial i)))) (main 50)
Nyní bude výpočet pokračovat pro libovolné hodnoty n:
$ bb 19_factorial_bigint.clj 0 ! = 1 1 ! = 1N 2 ! = 2N 3 ! = 6N 4 ! = 24N 5 ! = 120N 6 ! = 720N 7 ! = 5040N 8 ! = 40320N 9 ! = 362880N 10 ! = 3628800N 11 ! = 39916800N 12 ! = 479001600N 13 ! = 6227020800N 14 ! = 87178291200N 15 ! = 1307674368000N 16 ! = 20922789888000N 17 ! = 355687428096000N 18 ! = 6402373705728000N 19 ! = 121645100408832000N 20 ! = 2432902008176640000N 21 ! = 51090942171709440000N 22 ! = 1124000727777607680000N 23 ! = 25852016738884976640000N 24 ! = 620448401733239439360000N 25 ! = 15511210043330985984000000N 26 ! = 403291461126605635584000000N 27 ! = 10888869450418352160768000000N 28 ! = 304888344611713860501504000000N 29 ! = 8841761993739701954543616000000N 30 ! = 265252859812191058636308480000000N 31 ! = 8222838654177922817725562880000000N 32 ! = 263130836933693530167218012160000000N 33 ! = 8683317618811886495518194401280000000N 34 ! = 295232799039604140847618609643520000000N 35 ! = 10333147966386144929666651337523200000000N 36 ! = 371993326789901217467999448150835200000000N 37 ! = 13763753091226345046315979581580902400000000N 38 ! = 523022617466601111760007224100074291200000000N 39 ! = 20397882081197443358640281739902897356800000000N 40 ! = 815915283247897734345611269596115894272000000000N 41 ! = 33452526613163807108170062053440751665152000000000N 42 ! = 1405006117752879898543142606244511569936384000000000N 43 ! = 60415263063373835637355132068513997507264512000000000N 44 ! = 2658271574788448768043625811014615890319638528000000000N 45 ! = 119622220865480194561963161495657715064383733760000000000N 46 ! = 5502622159812088949850305428800254892961651752960000000000N 47 ! = 258623241511168180642964355153611979969197632389120000000000N 48 ! = 12413915592536072670862289047373375038521486354677760000000000N 49 ! = 608281864034267560872252163321295376887552831379210240000000000N 50 ! = 30414093201713378043612608166064768844377641568960512000000000000N
14. Datový typ double a počítání se zlomky
Jazyk Clojure podporuje i výpočty se zlomky (rational), což je opět ve světě LISPovských jazyků relativně často viděná vlastnost. Pokud totiž použijeme výchozí datový typ double, bude při výpočtech docházet ke všem chybám a problémům, které jsou způsobeny reprezentací hodnot dle IEEE 754 (proto se tento typ například nepoužívá v bankovnictví). Příkladem může být výpočet Pi iterativním vzorcem, u něhož nelze docílit vyšší přesnosti, než jaká odpovídá přesnosti a rozsahu typu double:
(defn compute-pi ([n pi] (loop [i 3 pi pi] (if (<= i (+ n 2)) (recur (+ i 2) (* pi (/ (- i 1) i) (/ (+ i 1) i))) pi))) ([n] (compute-pi n 4.0))) (doseq [i (range 0 20)] (let [n (bit-shift-left 1 i)] (println n "\t" (compute-pi n))))
Po určitém počtu iterací se již přesnost nezvyšuje:
1 3.5555555555555554 2 3.5555555555555554 4 3.4133333333333336 8 3.3023935500125976 16 3.2300364664117205 32 3.1881271694471423 64 3.1654820600348006 128 3.1536988490958002 256 3.1476868995564105 512 3.144650162517202 1024 3.1431240170281978 2048 3.1423589891217905 4096 3.141975985005618 8192 3.1417843602347597 16384 3.1416885171496745 32768 3.141640587929478 65536 3.1416166213995473 131072 3.1416046376545177 262144 3.141598645661904 524288 3.1415956496356134
Výpočet ovšem můžeme provést se zlomky a to tak, že ve zdrojovém kódu nikde nepoužijeme typ double. Potom například zápis (/ x y) znamená, že výsledkem výpočtu bude zlomek (zkrácený do minimální podoby). Povšimněte si, že se tím řeší všechny obvyklé problémy, které dělení v programovacích jazycích způsobuje – typickým příkladem je Python s velmi problematickým přístupem.
(defn compute-pi ([n pi] (loop [i 3 pi pi] (if (<= i (+ n 2)) (recur (+ i 2) (* pi (/ (- i 1) i) (/ (+ i 1) i))) pi))) ([n] (compute-pi n 4))) (doseq [i (range 0 6)] (let [n (bit-shift-left 1 i)] (println n "\t" (compute-pi n))))
Výsledky nyní budou vždy ve tvaru (zkráceného) zlomku:
1 32/9 2 32/9 4 256/75 8 65536/19845 16 4294967296/1329696225 32 18446744073709551616/5786075364399106425 ... ... ...
15. Výpočty ve více vláknech – map versus pmap
Velkou předností Babashky oproti jiným jednodušším technologiím je možnost spuštění částí algoritmu ve více vláknech. V jazyce Clojure k tomuto účelu existuje několik funkcí a maker. Pravděpodobně nejjednodušeji je použitelná funkce nazvaná pmap, která je obdobou funkce vyššího řádu map, ovšem s tím, že zpracování vstupu (aplikace funkce) je prováděno ve větším množství vláken přidělených z thread poolu (nemusí se tedy vždy vytvářet znovu). Nejprve si ukažme, jakým způsobem by bylo možné vypočítat konstantu Pi algoritmem popsaným zde pro počet iterací mezi hodnotou 1000000 a 1000020. Jedná se o vysoké počty iterací, takže sekvenční výpočet pro vstup 1000000, 1000001 atd. nebude příliš rychlý:
(defn compute-pi ([n pi] (loop [i 3 pi pi] (if (<= i (+ n 2)) (recur (+ i 2) (* pi (/ (- i 1) i) (/ (+ i 1) i))) pi))) ([n] (compute-pi n 4.0))) (let [n (range 1000000 1000020) results (doall (map #(compute-pi %) n))] (doseq [pi results] (println pi)))
Celý výpočet lze ovšem provést i paralelně a to velmi jednoduše – náhradou funkce map za funkci pmap neboli „parallel map“:
(defn compute-pi ([n pi] (loop [i 3 pi pi] (if (<= i (+ n 2)) (recur (+ i 2) (* pi (/ (- i 1) i) (/ (+ i 1) i))) pi))) ([n] (compute-pi n 4.0))) (let [n (range 1000000 1000020) results (doall (pmap #(compute-pi %) n))] (doseq [pi results] (println pi)))
Nyní si oba časy výpočtu můžeme porovnat. Nejprve sekvenční výpočet založený na funkci map (které se předala funkce anonymní – znak # lze číst jako λ):
$ time bb 22_sequential_map.clj 3.141594224383251 3.14159422438011 3.14159422438011 3.141594224376968 3.141594224376968 3.1415942243738266 3.1415942243738266 3.1415942243706847 3.1415942243706847 3.141594224367543 3.141594224367543 3.1415942243644017 3.1415942243644017 3.1415942243612593 3.1415942243612593 3.141594224358117 3.141594224358117 3.141594224354974 3.141594224354974 3.1415942243518313 real 1m15.040s user 1m14.825s sys 0m0.088s
Ve druhém případě je použit výpočet paralelní:
$ time bb 23_parallel_map.clj 3.141594224383251 3.14159422438011 3.14159422438011 3.141594224376968 3.141594224376968 3.1415942243738266 3.1415942243738266 3.1415942243706847 3.1415942243706847 3.141594224367543 3.141594224367543 3.1415942243644017 3.1415942243644017 3.1415942243612593 3.1415942243612593 3.141594224358117 3.141594224358117 3.141594224354974 3.141594224354974 3.1415942243518313 real 0m41.497s user 1m40.592s sys 0m3.002s
16. Porovnání rychlosti výpočtů Babashky s implementací Clojure pro JVM
V úvodní kapitole jsme si řekli, že se Babashka hodí především pro spouštění takových skriptů, v nichž se neprovádí složité výpočty. Je tomu tak z toho důvodu, že interpret (SCI) nemůže u dlouhodobějších výpočtů konkurovat původnímu Clojure, které provádí překlad do bajtkódu s jeho následným JITováním. Ostatně si to můžeme snadno otestovat – spustíme výpočet čísla Pi s běžným Clojure (starší verze 1.8.0 byla zvolena proto, že je spustitelná z příkazové řádky velmi snadno, bez nutnosti složitější manipulace s CLASSPATH):
$ time java -cp clojure-1.8.0.jar clojure.main 22_sequential_map.clj 3.141594224383251 3.14159422438011 3.14159422438011 3.141594224376968 3.141594224376968 3.1415942243738266 3.1415942243738266 3.1415942243706847 3.1415942243706847 3.141594224367543 3.141594224367543 3.1415942243644017 3.1415942243644017 3.1415942243612593 3.1415942243612593 3.141594224358117 3.141594224358117 3.141594224354974 3.141594224354974 3.1415942243518313 real 0m7.150s user 0m8.078s sys 0m0.280s
Výpočet (sekvenční) je znatelně rychlejší (osm sekund oproti minutě a patnácti sekundám), takže se ještě podívejme na možnosti paralelizace výpočtu s využitím funkce pmap:
$ time java -cp clojure-1.8.0.jar clojure.main 24_parallel_map_clojure.clj 3.141594224383251 3.14159422438011 3.14159422438011 3.141594224376968 3.141594224376968 3.1415942243738266 3.1415942243738266 3.1415942243706847 3.1415942243706847 3.141594224367543 3.141594224367543 3.1415942243644017 3.1415942243644017 3.1415942243612593 3.1415942243612593 3.141594224358117 3.141594224358117 3.141594224354974 3.141594224354974 3.1415942243518313 real 0m2.590s user 0m14.539s sys 0m0.585s
Nyní byl výpočet dokončen za necelé tři sekundy!
(defn compute-pi ([n pi] (loop [i 3 pi pi] (if (<= i (+ n 2)) (recur (+ i 2) (* pi (/ (- i 1) i) (/ (+ i 1) i))) pi))) ([n] (compute-pi n 4.0))) (let [n (range 1000000 1000020) results (doall (pmap #(compute-pi %) n))] (doseq [pi results] (println pi))) (System/exit 0)
17. Zobrazení nápovědy přímo v interaktivní smyčce REPL
Poslední užitečnou vlastností nástroje Babashka, s níž se dnes seznámíme, je možnost zobrazení nápovědy k implementovaným funkcím přímo z interaktivní smyčky REPL. To mj. znamená, že není zapotřebí dodávat další soubory s dokumentací, info stránky atd. Postačuje pouze spustit REPL:
$ bb
a následně použít makro doc pro zobrazení příslušné nápovědy:
user=> (doc first) ------------------------- clojure.core/first ([coll]) Returns the first item in the collection. Calls seq on its argument. If coll is nil, returns nil. nil user=> (doc rest) ------------------------- clojure.core/rest ([coll]) Returns a possibly empty seq of the items after the first. Calls seq on its argument. nil user=> (doc str) ------------------------- clojure.core/str ([] [x] [x & ys]) With no args, returns the empty string. With one arg x, returns x.toString(). (str nil) returns the empty string. With more than one arg, returns the concatenation of the str values of the args. nil
Nápovědu lze získat i k funkcím, které se nachází ve standardně dostupných balíčcích zmíněných v sedmé kapitole:
user=> (doc json/decode) ------------------------- cheshire.core/decode ([string] [string key-fn] [string key-fn array-coerce-fn]) Alias to parse-string for clojure-json users
18. Repositář s demonstračními příklady
Všechny dnes ukázané demonstrační příklady určené pro poslední verzi projektu Babashka byly uloženy do repositáře, který naleznete na adrese https://github.com/tisnik/clojure-examples, konkrétně do adresáře https://github.com/tisnik/clojure-examples/tree/master/babashka:
19. Odkazy na předchozí části seriálu o programovacím jazyku Clojure
- Clojure 1: Úvod
http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm/ - Clojure 2: Symboly, kolekce atd.
http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm-2-cast/ - 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/ - 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/ - 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/ - Clojure 6: Podpora pro paralelní programování
http://www.root.cz/clanky/programovaci-jazyk-clojure-6-futures-nejsou-jen-financni-derivaty/ - Clojure 7: Další funkce pro paralelní programování
http://www.root.cz/clanky/programovaci-jazyk-clojure-7-dalsi-podpurne-prostredky-pro-paralelni-programovani/ - 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/ - 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/ - Clojure 10: Kooperace mezi Clojure a Javou
http://www.root.cz/clanky/programovaci-jazyk-clojure-10-kooperace-mezi-clojure-a-javou-pokracovani/ - Clojure 11: Generátorová notace seznamu/list comprehension
http://www.root.cz/clanky/programovaci-jazyk-clojure-11-generatorova-notace-seznamu-list-comprehension/ - 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/ - 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/ - Clojure 14: Základy práce se systémem maker
http://www.root.cz/clanky/programovaci-jazyk-clojure-14-zaklady-prace-se-systemem-maker/ - Clojure 15: Tvorba uživatelských maker
http://www.root.cz/clanky/programovaci-jazyk-clojure-15-tvorba-uzivatelskych-maker/ - Programovací jazyk Clojure – triky při práci s řetězci
http://www.root.cz/clanky/programovaci-jazyk-clojure-triky-pri-praci-s-retezci/ - Programovací jazyk Clojure – triky při práci s kolekcemi
http://www.root.cz/clanky/programovaci-jazyk-clojure-triky-pri-praci-s-kolekcemi/ - Programovací jazyk Clojure – práce s mapami a množinami
http://www.root.cz/clanky/programovaci-jazyk-clojure-prace-s-mapami-a-mnozinami/ - Programovací jazyk Clojure – základy zpracování XML
http://www.root.cz/clanky/programovaci-jazyk-clojure-zaklady-zpracovani-xml/ - Programovací jazyk Clojure – testování s využitím knihovny Expectations
http://www.root.cz/clanky/programovaci-jazyk-clojure-testovani-s-vyuzitim-knihovny-expectations/ - 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/ - Enlive – výkonný šablonovací systém pro jazyk Clojure
http://www.root.cz/clanky/enlive-vykonny-sablonovaci-system-pro-jazyk-clojure/ - 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/ - Novinky v Clojure verze 1.8.0
http://www.root.cz/clanky/novinky-v-clojure-verze-1–8–0/ - 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/ - 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/ - 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/ - Vytváříme IRC bota v programovacím jazyce Clojure
http://www.root.cz/clanky/vytvarime-irc-bota-v-programovacim-jazyce-clojure/ - Gorilla REPL: interaktivní prostředí pro programovací jazyk Clojure
https://www.root.cz/clanky/gorilla-repl-interaktivni-prostredi-pro-programovaci-jazyk-clojure/ - Multimetody v Clojure aneb polymorfismus bez použití OOP
https://www.root.cz/clanky/multimetody-v-clojure-aneb-polymorfismus-bez-pouziti-oop/ - 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/ - Clojure 16: Složitější uživatelská makra
http://www.root.cz/clanky/programovaci-jazyk-clojure-16-slozitejsi-uzivatelska-makra/ - Clojure 17: Využití standardních maker v praxi
http://www.root.cz/clanky/programovaci-jazyk-clojure-17-vyuziti-standardnich-maker-v-praxi/ - Clojure 18: Základní techniky optimalizace aplikací
http://www.root.cz/clanky/programovaci-jazyk-clojure-18-zakladni-techniky-optimalizace-aplikaci/ - Clojure 19: Vývojová prostředí pro Clojure
http://www.root.cz/clanky/programovaci-jazyk-clojure-19-vyvojova-prostredi-pro-clojure/ - 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/ - Clojure 21: ClojureScript aneb překlad Clojure do JS
http://www.root.cz/clanky/programovaci-jazyk-clojure-21-clojurescript-aneb-preklad-clojure-do-javascriptu/ - Leiningen: nástroj pro správu projektů napsaných v Clojure
http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure/ - 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/ - 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/ - 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/ - 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/ - 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/ - Programovací jazyk Clojure a databáze (1.část)
http://www.root.cz/clanky/programovaci-jazyk-clojure-a-databaze-1-cast/ - Pluginy pro Leiningen
http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-pluginy-pro-leiningen/ - 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/ - 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/ - 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/ - 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/ - Seesaw: knihovna pro snadnou tvorbu GUI v jazyce Clojure
http://www.root.cz/clanky/seesaw-knihovna-pro-snadnou-tvorbu-gui-v-jazyce-clojure/ - 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/ - 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/ - Programovací jazyk Clojure a práce s Gitem
http://www.root.cz/clanky/programovaci-jazyk-clojure-a-prace-s-gitem/ - Programovací jazyk Clojure a práce s Gitem (2)
http://www.root.cz/clanky/programovaci-jazyk-clojure-a-prace-s-gitem-2/ - 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/ - Pixie: lehký skriptovací jazyk s „kouzelnými“ schopnostmi
https://www.root.cz/clanky/pixie-lehky-skriptovaci-jazyk-s-kouzelnymi-schopnostmi/ - Programovací jazyk Pixie: funkce ze základní knihovny a použití FFI
https://www.root.cz/clanky/programovaci-jazyk-pixie-funkce-ze-zakladni-knihovny-a-pouziti-ffi/ - Novinky v Clojure verze 1.9.0
https://www.root.cz/clanky/novinky-v-clojure-verze-1–9–0/ - 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/ - 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/ - 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/ - 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/ - Incanter: operace s maticemi
https://www.root.cz/clanky/incanter-operace-s-maticemi/ - Pixie: lehký skriptovací jazyk s „kouzelnými“ schopnostmi
https://www.root.cz/clanky/pixie-lehky-skriptovaci-jazyk-s-kouzelnymi-schopnostmi/ - Programovací jazyk Pixie: funkce ze základní knihovny a použití FFI
https://www.root.cz/clanky/programovaci-jazyk-pixie-funkce-ze-zakladni-knihovny-a-pouziti-ffi/ - Novinky v Clojure verze 1.9.0
https://www.root.cz/clanky/novinky-v-clojure-verze-1–9–0/ - 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/ - 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/ - 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/ - Interpret programovacího jazyka Clojure integrovaný do Jupyter Notebooku
https://www.root.cz/clanky/interpret-programovaciho-jazyka-clojure-integrovany-do-jupyter-notebooku/
20. Odkazy na Internetu
- babashka: A Clojure babushka for the grey areas of Bash
https://github.com/borkdude/babashka - Babashka and the Small Clojure Interpreter @ ClojureD 2020 (slajdy)
https://speakerdeck.com/borkdude/babashka-and-the-small-clojure-interpreter-at-clojured-2020 - Babashka: ukázky použití
https://github.com/borkdude/babashka/blob/master/doc/examples.md - clojureD 2020: „Babashka and Small Clojure Interpreter: Clojure in new contexts“ by Michiel Borkent
https://www.youtube.com/watch?v=Nw8aN-nrdEk&t=5s - Meetup #124 Babashka, implementing an nREPL server & game engines with Clojure
https://www.youtube.com/watch?v=0YmZYnwyHHc - The Last Programming Language (shrnutí vývoje programovacích jazyků)
https://www.youtube.com/watch?v=P2yr-3F6PQo - Shebang (Unix): Wikipedia EN
https://en.wikipedia.org/wiki/Shebang_(Unix) - Shebang (Unix): Wikipedia CZ
https://cs.wikipedia.org/wiki/Shebang_(Unix) - How to create Clojure notebooks in Jupyter
https://s01blog.wordpress.com/2017/12/10/how-to-create-clojure-notebooks-in-jupyter/ - Dokumentace k nástroji Conda
https://docs.conda.io/en/latest/ - Notebook interface
https://en.wikipedia.org/wiki/Notebook_interface - Jypyter: open source, interactive data science and scientific computing across over 40 programming languages
https://jupyter.org/ - Calysto Scheme
https://github.com/Calysto/calysto_scheme - scheme.py (základ projektu Calysto Scheme)
https://github.com/Calysto/calysto_scheme/blob/master/calysto_scheme/scheme.py - Humane test output for clojure.test
https://github.com/pjstadig/humane-test-output - iota
https://github.com/juxt/iota - 5 Differences between clojure.spec and Schema
https://lispcast.com/clojure.spec-vs-schema/ - Schema: Clojure(Script) library for declarative data description and validation
https://github.com/plumatic/schema - Zip archiv s Clojure 1.9.0
http://repo1.maven.org/maven2/org/clojure/clojure/1.9.0/clojure-1.9.0.zip - Clojure 1.9 is now available
https://clojure.org/news/2017/12/08/clojure19 - Deps and CLI Guide
https://clojure.org/guides/deps_and_cli - Changes to Clojure in Version 1.9
https://github.com/clojure/clojure/blob/master/changes.md - clojure.spec – Rationale and Overview
https://clojure.org/about/spec - Zip archiv s Clojure 1.8.0
http://repo1.maven.org/maven2/org/clojure/clojure/1.8.0/clojure-1.8.0.zip - Clojure 1.8 is now available
http://clojure.org/news/2016/01/19/clojure18 - Socket Server REPL
http://dev.clojure.org/display/design/Socket+Server+REPL - CLJ-1671: Clojure socket server
http://dev.clojure.org/jira/browse/CLJ-1671 - CLJ-1449: Add clojure.string functions for portability to ClojureScript
http://dev.clojure.org/jira/browse/CLJ-1449 - Launching a Socket Server
http://clojure.org/reference/repl_and_main#_launching_a_socket_server - API for clojure.string
http://clojure.github.io/clojure/branch-master/clojure.string-api.html - Clojars:
https://clojars.org/ - Seznam knihoven na Clojars:
https://clojars.org/projects - Clojure Cookbook: Templating HTML with Enlive
https://github.com/clojure-cookbook/clojure-cookbook/blob/master/07_webapps/7–11_enlive.asciidoc - An Introduction to Enlive
https://github.com/swannodette/enlive-tutorial/ - Enlive na GitHubu
https://github.com/cgrand/enlive - Expectations: příklady atd.
http://jayfields.com/expectations/ - Expectations na GitHubu
https://github.com/jaycfields/expectations - Lein-expectations na GitHubu
https://github.com/gar3thjon3s/lein-expectations - Testing Clojure With Expectations
https://semaphoreci.com/blog/2014/09/23/testing-clojure-with-expectations.html - Clojure testing TDD/BDD libraries: clojure.test vs Midje vs Expectations vs Speclj
https://www.reddit.com/r/Clojure/comments/1viilt/clojure_testing_tddbdd_libraries_clojuretest_vs/ - Testing: One assertion per test
http://blog.jayfields.com/2007/06/testing-one-assertion-per-test.html - Rewriting Your Test Suite in Clojure in 24 hours
http://blog.circleci.com/rewriting-your-test-suite-in-clojure-in-24-hours/ - Clojure doc: zipper
http://clojuredocs.org/clojure.zip/zipper - Clojure doc: parse
http://clojuredocs.org/clojure.xml/parse - Clojure doc: xml-zip
http://clojuredocs.org/clojure.zip/xml-zip - Clojure doc: xml-seq
http://clojuredocs.org/clojure.core/xml-seq - Parsing XML in Clojure
https://github.com/clojuredocs/guides - Clojure Zipper Over Nested Vector
https://vitalyper.wordpress.com/2010/11/23/clojure-zipper-over-nested-vector/ - Understanding Clojure's PersistentVector implementation
http://blog.higher-order.net/2009/02/01/understanding-clojures-persistentvector-implementation - Understanding Clojure's PersistentHashMap (deftwice…)
http://blog.higher-order.net/2009/09/08/understanding-clojures-persistenthashmap-deftwice.html - Assoc and Clojure's PersistentHashMap: part ii
http://blog.higher-order.net/2010/08/16/assoc-and-clojures-persistenthashmap-part-ii.html - Ideal Hashtrees (paper)
http://lampwww.epfl.ch/papers/idealhashtrees.pdf - Clojure home page
http://clojure.org/ - Clojure (downloads)
http://clojure.org/downloads - Clojure Sequences
http://clojure.org/sequences - Clojure Data Structures
http://clojure.org/data_structures - 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 - 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 - Clojure – Functional Programming for the JVM
http://java.ociweb.com/mark/clojure/article.html - Clojure quick reference
http://faustus.webatu.com/clj-quick-ref.html - 4Clojure
http://www.4clojure.com/ - ClojureDoc (rozcestník s dokumentací jazyka Clojure)
http://clojuredocs.org/ - Clojure (na Wikipedia EN)
http://en.wikipedia.org/wiki/Clojure - Clojure (na Wikipedia CS)
http://cs.wikipedia.org/wiki/Clojure - SICP (The Structure and Interpretation of Computer Programs)
http://mitpress.mit.edu/sicp/ - Pure function
http://en.wikipedia.org/wiki/Pure_function - Funkcionální programování
http://cs.wikipedia.org/wiki/Funkcionální_programování - Čistě funkcionální (datové struktury, jazyky, programování)
http://cs.wikipedia.org/wiki/Čistě_funkcionální - 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 - Clojure Macro Tutorial (Part II: The Compiler Strikes Back)
http://www.learningclojure.com/2010/09/clojure-macro-tutorial-part-ii-compiler.html - Clojure Macro Tutorial (Part III: Syntax Quote)
http://www.learningclojure.com/2010/09/clojure-macro-tutorial-part-ii-syntax.html - Tech behind Tech: Clojure Macros Simplified
http://techbehindtech.com/2010/09/28/clojure-macros-simplified/ - Fatvat – Exploring functional programming: Clojure Macros
http://www.fatvat.co.uk/2009/02/clojure-macros.html - Eulerovo číslo
http://cs.wikipedia.org/wiki/Eulerovo_číslo - List comprehension
http://en.wikipedia.org/wiki/List_comprehension - List Comprehensions in Clojure
http://asymmetrical-view.com/2008/11/18/list-comprehensions-in-clojure.html - Clojure Programming Concepts: List Comprehension
http://en.wikibooks.org/wiki/Clojure_Programming/Concepts#List_Comprehension - Clojure core API: for macro
http://clojure.github.com/clojure/clojure.core-api.html#clojure.core/for - cirrus machina – The Clojure for macro
http://www.cirrusmachina.com/blog/comment/the-clojure-for-macro/ - Riastradh's Lisp Style Rules
http://mumble.net/~campbell/scheme/style.txt - Dynamic Languages Strike Back
http://steve-yegge.blogspot.cz/2008/05/dynamic-languages-strike-back.html - Scripting: Higher Level Programming for the 21st Century
http://www.tcl.tk/doc/scripting.html - Java Virtual Machine Support for Non-Java Languages
http://docs.oracle.com/javase/7/docs/technotes/guides/vm/multiple-language-support.html - Třída java.lang.String
http://docs.oracle.com/javase/7/docs/api/java/lang/String.html - Třída java.lang.StringBuffer
http://docs.oracle.com/javase/7/docs/api/java/lang/StringBuffer.html - Třída java.lang.StringBuilder
http://docs.oracle.com/javase/7/docs/api/java/lang/StringBuilder.html - StringBuffer versus String
http://www.javaworld.com/article/2076072/build-ci-sdlc/stringbuffer-versus-string.html - Threading macro (dokumentace k jazyku Clojure)
https://clojure.github.io/clojure/clojure.core-api.html#clojure.core/-> - Understanding the Clojure → macro
http://blog.fogus.me/2009/09/04/understanding-the-clojure-macro/ - clojure.inspector
http://clojure.github.io/clojure/clojure.inspector-api.html - The Clojure Toolbox
http://www.clojure-toolbox.com/ - Unit Testing in Clojure
http://nakkaya.com/2009/11/18/unit-testing-in-clojure/ - Testing in Clojure (Part-1: Unit testing)
http://blog.knoldus.com/2014/03/22/testing-in-clojure-part-1-unit-testing/ - API for clojure.test – Clojure v1.6 (stable)
https://clojure.github.io/clojure/clojure.test-api.html - Leiningen: úvodní stránka
http://leiningen.org/ - Leiningen: Git repository
https://github.com/technomancy/leiningen - leiningen-win-installer
http://leiningen-win-installer.djpowell.net/ - Clojure.org: Vars and the Global Environment
http://clojure.org/Vars - Clojure.org: Refs and Transactions
http://clojure.org/Refs - Clojure.org: Atoms
http://clojure.org/Atoms - Clojure.org: Agents as Asynchronous Actions
http://clojure.org/agents - Transient Data Structures
http://clojure.org/transients