Obsah
1. Programovací jazyk Joker: dialekt Clojure naprogramovaný v Go
2. Porovnání Jokeru s programovacím jazykem Clojure
3. Instalace interpretru programovacího jazyka Joker
4. Spuštění interaktivní smyčky REPL
7. Základní jazykové konstrukce podporované Jokerem
13. Vliv použitého datového typu na funkci pro výpočet faktoriálu
14. Výpočet konstanty Pi Wallisovým součinem: použití reálných čísel a zlomků
18. Repositář s demonstračními příklady
1. Programovací jazyk Joker: dialekt Clojure naprogramovaný v Go
V dnešní části seriálu o LISPovských programovacích jazycích se seznámíme se základními vlastnostmi programovacího jazyka pojmenovaného Joker [1]. Jedná se o jeden z jazyků, které se snaží používat syntaxi i sémantiku podobnou či v ideálním případě zcela shodnou s programovacím jazykem Clojure, který je (vedle některých dialektů Scheme a Common LISPu) dnes pravděpodobně nejpopulárnějším LISPovským programovacím jazykem, jenž se dokonce umisťuje i relativně vysoko v různých statistikách (na druhou stranu ovšem vypadl z první padesátky Tiobe indexu. Ovšem zatímco jazyk Clojure existuje ve třech variantách – pro JVM (překlad do bajtkódu), pro CLR (taktéž překlad do bajtkódu) a pro interpretry JavaScriptu (ClojureScript), je tomu v případě jazyka Joker jinak, protože Joker je vyvinut v programovacím jazyku Go a – alespoň prozatím – je implementován jako prostý interpret, který neprovádí překlad do bajtkódu ani do nativního kódu.
Tento přístup má své přednosti, ale pochopitelně i některé zápory. Mezi přednosti patří především velmi rychlý start interpretru, a to jak v porovnání se samotným Clojure (pro JVM), tak i například v porovnání s jazykem Racket, jemuž jsme se věnovali v předchozích částech tohoto seriálu ([2], [3], [4] a [5]) a k jehož popisu se ještě později vrátíme. Ovšem „pouhá“ interpretace kódu bez jeho (mezi)překladu a případných optimalizací má negativní vliv na celkový výkon aplikace. Projeví se to zejména u složitějších výpočtů, popř. u zpracování rozsáhlejších datových struktur, nicméně pro mnoho reálných aplikací by se nemuselo jednat o zásadní problém.
Další vlastnost jazyka Joker však může být užitečná i pro ty programátory, kteří používají originální jazyk Clojure. Joker totiž umožňuje spustit linter, který dokáže zkontrolovat korektnost zapsaného zdrojového kódu. A díky tomu, že se linter spouští velmi rychle (pod sekundu), lze tento nástroj použít i pro rychlou kontrolu zdrojových kódů aplikace psaných přímo v Clojure. Spuštění interpretru Clojure je totiž naproti tomu mnohem pomalejší a náročnější na systémové zdroje. Navíc je možné linter použít i pro rychlou kontrolu souborů uložených ve formátu EDN (Extensible Data Notation), který je taktéž založen na syntaxi programovacího jazyka Clojure. Bližší informace o tomto formátu, s nímž se ve světě Clojure relativně často setkáme, lze najít na stránce https://github.com/edn-format/edn.
2. Porovnání Jokeru s programovacím jazykem Clojure
Programovací jazyk Joker se v některých ohledech od jazyka Clojure odlišuje, což je však většinou pochopitelné, protože ne všechny vlastnosti JVM, popř. CLR je možné „emulovat“ v interpretru, který k těmto nástrojům nemá přístup. Některé vlastnosti Jokeru, například nabídka primitivních datových typů, je přímo ovlivněna vlastnostmi programovacího jazyka Go. Ovšem nejdůležitějším rozdílem (alespoň v současné verzi Jokeru) je neexistence podpory pro souběžný běh několika vláken, což je vlastnost podporovaná přímo v základech jazyka Clojure – viz například transakční paměť, agenti, pmap a pcalls, futures a promise atd. atd. Je to zajímavé, protože Joker má k dispozici všechny prostředky jazyka Go, především gorutiny a kanály, takže by alespoň nějakou podporu pro souběžné spouštění funkcí bylo možné naprogramovat. Možná se dočkáme až v některé vyšší verzi tohoto jazyka. S dalšími rozdíly mezi původním Clojure a Jokerem se postupně seznámíme v navazujícím textu.
- Clojure vs Go – Clash of the Titans!
https://our.status.im/clojure-vs-go/ - How do you see future of Clojure compared to Golang?
https://www.reddit.com/r/Clojure/comments/5uftns/how_do_you_see_future_of_clojure_compared_to/ - Choosing your future tech stack: Clojure vs Elixir vs Go
https://smashingboxes.com/blog/choosing-your-future-tech-stack-clojure-vs-elixir-vs-go/ - Imagine you have to select a programming language in 2019
https://medium.com/@yuliaoletskaya/imagine-you-have-to-select-a-programming-language-in-2019–162ddcbb6cf
3. Instalace interpretru programovacího jazyka Joker
Programovací jazyk Joker s poměrně velkou pravděpodobností nenaleznete v oficiálních repositářích vaší distribuce, takže je nutné instalaci provést odlišným způsobem. Archivy s binárním (spustitelným) souborem obsahujícím interpret jazyka Joker i jeho základní knihovny (vše v jednom binárním spustitelném souboru) jsou umístěny na adrese https://github.com/candid82/joker/releases/tag/v0.12.7 (tabulka s archivy je zobrazena na konci stránky). Stažení archivu určeného pro 64bitovou platformu (x86–64) s Linuxem je tudíž velmi snadné:
$ wget https://github.com/candid82/joker/releases/download/v0.12.7/joker-0.12.7-linux-amd64.zip
Po rozbalení staženého archivu postačuje interpret (představovaný souborem joker) umístit do některého adresáře, na který ukazuje proměnná prostředí PATH (může se jednat například o /usr/local/bin, ~/bin atd. podle konkrétního uživatelského nastavení):
$ unzip joker-0.12.7-linux-amd64.zip $ mv joker ~/bin
V případě, že preferujete vlastní překlad interpretru, musíte mít nainstalovány standardní nástroje programovacího jazyka Go dostupné přímo v repositáři vaší distribuce, popř. nabízené na stránce https://golang.org/dl/. Po instalaci jazyka Go je nutné provést nastavení proměnné prostředí GOPATH, což je problematika, kterou jsme se podrobněji věnovali v úvodním článku, který zahájil paralelně běžící seriál o Go. Samotný překlad se provede těmito pomocí kroků popsaných v následujících odstavcích.
Samotný proces překladu Jokeru není příliš složitý ani zdlouhavý:
Před vlastním překladem nejdříve získáme zdrojové kódy, a to s využitím příkazu go get:
$ go get -d github.com/candid82/joker
Následně přejdeme do adresáře, který se předchozím příkazem vytvořil a naplnil zdrojovými kódy:
$ cd $GOPATH/src/github.com/candid82/joker
Překlad spustíme přes skript run.sh a standardním příkazem go install:
$ ./run.sh --version && go install
Výsledkem překladu by měl být spustitelný binární soubor o velikosti přibližně 15 MB. Jedná se o poměrně značnou velikost, což je způsobeno tím, že aplikace vytvořené v Go jsou překládány a linkovány se všemi knihovnami, včetně knihovny pro automatickou správu paměti (GC) apod.:
$ ls -l joker -rwxr-xr-x 1 tester tester 15248928 zář 3 06:53 joker
4. Spuštění interaktivní smyčky REPL
Po (doufejme že bezproblémovém a úspěšném) překladu a instalaci programovacího jazyka Joker si můžeme vyzkoušet spustit interaktivní smyčku REPL, kterou je tento jazyk – ostatně jako každý správný jazyk patřící do LISPovské rodiny – vybaven. V případě, že binární soubor s interpretrem leží v nějakém adresáři umístěném do proměnné prostředí PATH, je spuštění REPLu snadné a navíc prakticky okamžité:
$ ./joker Welcome to joker v0.12.7. Use EOF (Ctrl-D) or SIGINT (Ctrl-C) to exit.
Alternativně (pokud byl překlad proveden uživatelem ze zdrojových kódů):
$ $GOPATH/bin/joker Welcome to joker v0.12.7. Use EOF (Ctrl-D) or SIGINT (Ctrl-C) to exit.
Pod uvítací zprávou se zobrazí výzva (prompt), která jako by z oka vypadla výzvě známé z programovacího jazyka Clojure:
user=>
$ lein repl
Ve druhém případě trvá spuštění REPLu cca pět sekund i na poměrně rychlém stroji s mikroprocesorem i7 taktovaným na 2,9 GHz, ovšem na pomalejších počítačích může spuštění interaktivní smyčky REPL trvat i více než deset sekund, což je (například pro rychlé otestování nějaké myšlenky) poměrně nepříjemné zdržení. Naproti tomu se interpret jazyka Joker spustí i na pomalejších počítačích takřka okamžitě, takže se skutečně jedná o výborný doplněk ke Clojure určený například k rychlému otestování nových nápadů apod.
5. Spuštění skriptu
Kromě přímého přechodu do interaktivní smyčky REPL je možné spustit nějaký skript naprogramovaný v jazyce Joker. Spuštění skriptu je snadné a přímočaré:
$ joker jméno_skriptu
Skripty vytvořené v jazyce Joker by měly mít koncovku .joker, ovšem bez problémů lze použít i koncovku .clj, která může být výhodnější; už jen z toho důvodu, že tuto koncovku rozpoznávají mnohé programátorské textové editory.
Příklad spuštění skriptu pro výpočet hodnoty Pi:
$ ./joker pi_1.clj 1 4.000000 2 3.555556 4 3.413333 8 3.302394 16 3.230036 32 3.188127 64 3.165482 128 3.153699 256 3.147687 512 3.144650 1024 3.143124 2048 3.142359 4096 3.141976 8192 3.141784 16384 3.141689 32768 3.141641 65536 3.141617
6. Režim linteru
Jak jsme si již řekli v úvodní kapitole, je možné interpret programovacího jazyka Joker přepnout do režimu linteru, v němž se provádí kontrola zdrojového kódu (či dat), ovšem bez jeho spuštění. Základní varianta příkazu vypadá takto:
$ joker --lint jméno_skriptu
Ve skutečnosti je ještě možné přesně specifikovat dialekt určující, jaký zdrojový kód je očekáván na vstupu:
$ joker --lint --dialect dialekt jméno_skriptu
Mezi podporované dialekty patří:
# | Volba –dialect | Význam |
---|---|---|
1 | clj | zdrojový soubor určený pro Clojure |
2 | cljs | zdrojový soubor určený pro ClojureScript |
3 | joker | zdrojový soubor určený pro Joker |
4 | edn | soubor ve formátu EDN (Extensible Data Notation) |
Příklad výstupu linteru:
raster_renderer.clj:177:21: Parse warning: Wrong number of args (7) passed to graph-generator.raster-renderer/draw-line raster_renderer.clj:178:21: Parse warning: Wrong number of args (7) passed to graph-generator.raster-renderer/draw-circle raster_renderer.clj:179:21: Parse warning: Wrong number of args (7) passed to graph-generator.raster-renderer/draw-arc raster_renderer.clj:180:21: Parse warning: Wrong number of args (7) passed to graph-generator.raster-renderer/draw-text raster_renderer.clj:174:13: Parse warning: unused binding: i raster_renderer.clj:303:13: Parse warning: unused binding: i raster_renderer.clj:432:19: Parse warning: unused binding: bounds raster_renderer.clj:771:11: Parse warning: unused binding: room-id raster_renderer.clj:29:12: Parse warning: unused namespace graph-generator.db-interface
7. Základní jazykové konstrukce podporované Jokerem
V této kapitole se zmíníme o základních konceptech, na nichž je jazyk Joker postaven. Samotný popis jednotlivých konstrukcí bude poměrně stručný, a to z toho důvodu, že se Joker v mnoha ohledech podobá programovacímu jazyku Clojure, kterému jsme se na stránkách Rootu zevrubně věnovali.
Nejprve se ve stručnosti zmíníme o primitivních datových typech. Ty jsou v Jokeru podobné, jako je tomu v Go, ovšem konkrétní implementace (a někdy i chování) se může nepatrně odlišovat:
; hodnota nil použitelná i v logických výrazech user=> nil nil ; celočíselný typ int v Go user=> 42 42 ; je možné použít i hexadecimální zápis celých hodnot user=> 0x2a 42 ; Joker podporuje zlomky (typ ratio) s prakticky neomezeným rozsahem hodnot čitatele i jmenovatele user=> 1/3 1/3 ; a samozřejmě i čísla s plovoucí řádovou čárkou (typ float64) user=> 3.1415 3.1415 ; řetězce user=> "foo bar baz" "foo bar baz" ; funkce count pracuje korektně i s Unicode řetězci user> (count "abcde") 5 user> (count "ěščřž") 5 ; symboly user=> :foo-bar-baz :foo-bar-baz
Zapomenout nesmíme ani na celočíselný typ bez omezení rozsahu reprezentovatelných hodnot. Interně je tento typ implementován pomocí balíčku big.Int:
user=> 1N 1N
Nechybí ani numerický typ s plovoucí řádovou čárkou a prakticky nekonečným rozsahem, popř. přesností. Zde se používá postfixový znak M a interně je tento typ implementován pomocí balíčku big.Float:
user=> 1M 1M user=> 1.5e100M 1.5e+100M
Dále si v této kapitole ukažme základní volání funkce:
user=> (println "Hello world") Hello world nil
Důležitá je i speciální forma nazvaná def. Ta se většinou používá k navázání libovolné hodnoty (například čísla, pravdivostní hodnoty, řetězce, seznamu a jak uvidíme dále, tak i funkce) na symbol. Méně časté je použití této speciální formy k pouhému vytvoření symbolu. V případě, že Joker vyhodnotí („spustí“) tuto speciální formu, dojde k vytvoření nové globální proměnné v aktuálně nastaveném jmenném prostoru (nejde tedy o skutečnou globální proměnnou, ale o proměnnou identifikovatelnou přes jmenný prostor – viz další text) a k inicializaci této proměnné. Pokud již globální proměnná stejného jména existuje, dojde k „přepisu“ její hodnoty. Ve skutečnosti však stará hodnota nemusí přestat existovat, protože může být navázána na další proměnné:
user=> (def x (range 10)) #'user/x
Vyhodnocení hodnoty navázané na symbol:
user=> x (0 1 2 3 4 5 6 7 8 9)
Nepatrně složitější příklad:
user> (def x 6) #'user/x user> (def y 7) #'user/y user> (def answer (* x y)) #'user/answer user> answer 42
Ke globální proměnné lze přiřadit i takzvaný dokumentační řetězec a ten následně získat makrem doc:
user> (def answer "Odpoved na otazku o ... vesmiru, zivote a vubec" 42) #'user/answer user> (doc answer) ------------------------- user/answer Odpoved na otazku o ... vesmiru, zivote a vubec
8. Jmenné prostory
V předchozí kapitole jsme se poprvé explicitně zmínili o takzvaných jmenných prostorech. Jmenné prostory byly do programovacího jazyka Joker (resp. do Clojure a potom do Jokeru) přidány zejména z toho důvodu, že použití globálních symbolů je v reálných programech velmi nebezpečné a to zejména proto, že jiná část programu, která může být klidně vytvořena i jiným vývojářem, může nechtěně předeklarovat již existující globální symbol. Co je ještě horší – tato předeklarace nemusí nutně vést k okamžité chybě při práci s programem (ideálně při jeho testování), ale může se projevit až při určité shodě okolností – podle všeobecně platného zákona tedy ve chvíli, kdy se aplikace předvádí šéfovi či zákazníkovi :-). Připomeňme si, že mezi globální symboly patří i symboly představující jména funkcí, takže je asi představitelné, co by se stalo, kdyby nějaká importovaná knihovna náhodou obsahovala funkci pojmenovanou stejně, jako funkce vytvořená programátorem vyvíjené aplikace. Jmenné prostory proto představují jeden z možných způsobů, jak tento problém poměrně elegantně vyřešit (i když práce s nimi není vždy jednoduchá).
My jsme se již vlastně s jedním jmenným prostorem setkali v textech vypisovaných smyčkou REPL, i když jsme si prozatím nevysvětlili, že se skutečně jedná o jmenný symbol. Při používání smyčky REPL je totiž jméno aktuálního jmenného prostoru vypisováno jako součást výzvy (prompt):
user=>
V programovacím jazyku Joker je možné vytvořit takřka libovolný počet jmenných prostorů a posléze se mezi těmito jmennými prostory přepínat, tj. lze zvolit, který jmenný prostor bude jmenným prostorem aktuálním. Pro tuto činnost se používá makro nazvané ns:
(ns název_jmenného_prostoru)
Podívejme se nyní na jednoduchý demonstrační příklad, v němž jsou vytvořeny dvě globální proměnné nazvané answer. Každé proměnné je přiřazena jiná hodnota a každá proměnná tudíž musí být uložena v jiném jmenném prostoru. Povšimněte si taktéž toho, jak se změní výzva (prompt) při přepnutí aktuálního jmenného prostoru:
; vytvoření globální proměnné umístěné ve jmenném prostoru "user" user=> (def answer 42) #'user/answer ; jméno proměnné se vyhodnotí na hodnotu proměnné user=> answer 42 ; vytvoření nového jmenného prostoru nazvaného "novy" user=> (ns novy) nil ; lze v tomto jmenném prostoru vyhodnotit (=najít) proměnnou answer? novy=> answer CompilerException java.lang.RuntimeException: Unable to resolve symbol: answer in this context, compiling:(NO_SOURCE_PATH:0) ; vytvoření nové globální proměnné ve jmenném prostoru "novy" novy=> (def answer "?") #'novy/answer ; její hodnotu nyní můžeme získat (vyhodnotit), aniž by došlo k chybě novy=> answer "?" ; přepnutí jmenného prostoru novy=> (ns user) nil ; nyní je opět viditelná první globální proměnná se jménem answer user=> answer 42
Ve skutečnosti však nejsou jednotlivé jmenné prostory od sebe izolovány, takže se můžeme odkazovat na symbol umístěný v jiném jmenném prostoru pomocí zápisu jmenný_prostor/symbol. Ostatně i kvůli podpoře tohoto způsobu zápisu není možné použít znak / ve jméně žádného symbolu (znaky * či – je však možné použít, což se taktéž často děje, protože – se používá pro oddělení jednotlivých slov v názvu symbolu a hvězdička je podle konvencí používána pro konstanty). Podívejme se nyní na způsob využití zápisu jmenný_prostor/symbol:
; přepnutí jmenného prostoru user=> (ns user) nil ; proměnná z aktuálního jmenného prostoru user=> answer 42 ; proměnná z jiného jmenného prostoru user=> novy/answer "?" ; přepnutí jmenného prostoru user=> (ns novy) nil ; proměnná z aktuálního jmenného prostoru novy=> answer "?" ; proměnná z aktuálního jmenného prostoru novy=> novy/answer "?" ; proměnná z jiného jmenného prostoru novy=> user/answer 42
9. Práce se seznamy
Programovací jazyk Joker podporuje všechny základní strukturované datové typy, které známe z jazyka Clojure. Jedná se o seznamy, vektory, mapy i množiny:
# | Typ kolekce | Zápis konstruktoru |
---|---|---|
1 | Seznam | '(prvky) |
2 | Vektor | [prvky] |
3 | Mapa | {dvojice klíč-hodnota} |
4 | Množina | #{unikátní prvky} |
Základním strukturovaným datovým typem každého LISPovského programovacího jazyka jsou nepochybně seznamy (list). Jejich zpracování je v jazyku Joker podobné jako v Clojure – prvky seznamů se zapisují do kulatých závorek, což je ovšem stejná syntaxe, jako zápis volání funkce. Aby se seznam nevyhodnocoval jako funkce (tedy aby první prvek seznamu nebyl chápán jako jméno funkce), je nutné před jeho deklaraci zapsat znak ' (apostrof) nebo použít speciální formu quote.
Pochopitelně je podporován i koncept prázdného seznamu, který se zapisuje ve formě prázdného páru kulatých závorek:
; prázdný seznam user=> '() () ; prázdný seznam user=> (quote ()) ()
Seznam obsahující čtveřici numerických hodnot se vytvoří následujícím způsobem:
; seznam čísel user=> '(1 2 3 4) (1 2 3 4) ; seznam čísel user=> (quote (1 2 3 4)) (1 2 3 4)
Seznam obsahující trojici řetězců vytvoříme naprosto stejným postupem:
; seznam řetězců user=> '("prvni" "druhy" "treti") ("prvni" "druhy" "treti")
Poměrně často se setkáme s použitím keywords, které jsou určeny pro reprezentaci neměnných a současně i unikátních hodnot (unikátních v rámci celého programu, popř. jmenných prostorů):
; seznam "keywords" user=> '(:prvni :druhy :treti) (:prvni :druhy :treti) user=> (quote (:foo :bar :baz)) (:foo :bar :baz)
Dále je pochopitelně možné do seznamů ukládat proměnné, jejichž hodnota však není vyhodnocována. To je ostatně logické, protože není vyhodnocen ani samotný seznam a pravidla pro vyhodnocení jsou v LISPovských jazycích jednoduchá (až na makra, která naopak takové vyhodnocení umožňují):
user=> (def positionX 1) #'user/positionX user=> (def positionY 2) #'user/positionY user=> (def positionZ 3) #'user/positionZ
Vytvoření seznamu, v němž jsou uloženy proměnné:
; seznam s proměnnými user=> '(positionX positionY positionZ) (positionX positionY positionZ)
Ukažme si ještě několik příkladů vnořených seznamů:
; vnořené seznamy user=> '( '(:x :y) '(:z :w) ) ((quote (:x :y)) (quote (:z :w)))
Díky tomu, že se nevyhodnocuje už vnější seznam, můžeme použít i následující zápis (který však není ekvivalentní s předchozím zápisem – výsledkem je odlišná datová struktura):
; vnořené seznamy user=> '( '(:x :y) '(:z :w) ) ((:x :y) (:z :w))
Délka seznamu:
user> (count '(1 2 3 4)) 4
Zploštění seznamu je další relativně často používaná operace:
user> (flatten '( (:x :y) (:z :w) )) (:x :y :z :w)
10. Vektory
Další důležitou datovou strukturou, která je převzata z programovacího jazyka Clojure, jsou vektory (vectors). Prvky vektorů se zapisují do hranatých závorek a vzhledem k tomu, že se jedná o zápis odlišný od volání funkce (na rozdíl od seznamů), není nutné před vektor psát ani apostrof ani speciální formu quote. Prázdný vektor se zapíše takto:
; prázdný vektor user=> [] []
Vektor se čtveřicí celočíselných hodnot můžeme zapsat následovně:
; vektor čísel user=> [1 2 3 4] [1 2 3 4]
Pochopitelně nám nic nebrání si vytvořit vektor z řetězců
; vektor řetězců user=> ["prvni" "druhy" "treti"] ["prvni" "druhy" "treti"]
Vektor obsahující „keywords“:
; vektor "keywords" user=> [:prvni :druhy :treti] [:prvni :druhy :treti]
A pochopitelně i vektor proměnných:
user=> (def positionX 1) #'user/positionX user=> (def positionY 2) #'user/positionY user=> (def positionZ 3) #'user/positionZ ; vektor proměnných user=> [positionX positionY positionZ] [1 2 3]
Další často vyžadované operace se seznamy:
; zploštění vektorů (výsledkem je seznam!) user> (flatten [[:x :y] [:z :w]]) (:x :y :z :w) ; přístup k prvku vektoru user> (nth [1 2 3 4] 2) 3 ; neexistující prvek user> (nth [1 2 3 4] 10) <joker.core>:693:25: Eval error: Index 10 is out of bounds [0..3] Stacktrace: global </gcrepl>/gc:26:1 core/nth </gcjoker.core>/gc:693:25 ; přístup k prvku vektoru bez rizika vzniku chyb user> (get [1 2 3 4] 2) 3 ; neexistující prvek user> (get [1 2 3 4] 10) nil
11. Mapy
Mapa, popř. též asociativní pole, se v reálných aplikacích používá poměrně často a existuje pro ni i speciální konstrukce spočívající v použití složených závorek, do nichž se zapíšou dvojice klíč-hodnota:
; prázdná mapa user=> {} {}
Mapa s řetězci:
; mapování typu string-string user=> {"prvni" "first" "druhy" "second" "treti" "third"} {"prvni" "first", "druhy" "second", "treti" "third"}
Lze použít i čárky, které zajistí větší čitelnost (je zřejmé, kde končí která dvojice:)
; mapování typu string-string user=> {"prvni" "first", "druhy" "second", "treti" "third"} {"prvni" "first", "druhy" "second", "treti" "third"}
Dále je vhodné si uvědomit, že prvky mapy jsou nejdříve vyhodnoceny:
; mapa s vyhodnocením proměnných user=> {"X" positionX "y" positionY "z" positionZ} {"X" 1, "y" 2, "z" 3}
Převod vektorů/seznamů klíčů a vektorů/seznamů hodnot na mapu:
; stejný počet klíčů i hodnot user> (zipmap [1 2 3 4] [5 6 7 8]) {1 5, 2 6, 3 7, 4 8} ; větší počet hodnot user> (zipmap [1 2 3 4] [5 6 7 8 9 0]) {1 5, 2 6, 3 7, 4 8}
Přístup k hodnotám uloženým v mapě:
user> (def slova {"prvni" "first", "druhy" "second", "treti" "third"}) #'user/slova user> (get slova "prvni") "first" user> (get slova "foobar") nil
Test na existenci prvku (podle klíče):
user> (def slova {"prvni" "first", "druhy" "second", "treti" "third"}) #'user/slova user> (contains? slova "prvni") true user> (contains? slova "foobar") false
Výběr většího množství prvků:
user> (select-keys slova ["prvni" "treti"]) {"prvni" "first", "treti" "third"}
12. Množiny
V jazyku Joker lze pochopitelně pracovat i s množinami. Ty se vytváří konstruktorem #{}:
; Množina user=> #{"prvni" "druhy" "treti"} #{"prvni" "druhy" "treti"}
Prvky v množině nesmí být duplikátní!
Kontrolují se samozřejmě shodné hodnoty literálů:
user=> #{1 1 3} Read error: Duplicate set element 1
Řetězce jsou, jak již víme, taktéž literály (zde je duplikován řetězec „nesmi“):
user=> #{"nesmi" "mit" "dva" "stejne" "prvky" "skutecne" "nesmi"} Read error: Duplicate set element nesmi
Joker poctivě zkontroluje i ekvivalenci seznamů atd.:
user=> #{ '(:stejny :seznam) '(:stejny :seznam) } Read error: Duplicate set element (quote (:stejny :seznam))
user=> #{1 1 3} IllegalArgumentException Duplicate key: 1 clojure.lang.PersistentHashSet.create WithCheck (PersistentHashSet.java:68)
13. Vliv použitého datového typu na funkci pro výpočet faktoriálu
V sedmé kapitole jsme se mj. zmínili o tom, že v jazyku Joker existuje hned několik datových typů určených pro reprezentaci numerických hodnot. Nyní si ukážeme, jaký vliv má zvolený datový typ na některé výpočty. Připomeňme si deklaraci funkce pro výpočet faktoriálu. Její zkrácená varianta zapsatelná na jediný řádek využívá funkci vyššího řádu reduce a generátor sekvence range. Všechny výpočty přitom probíhají s využitím datového typu „celé číslo“, který má ovšem omezený rozsah hodnot, což se projeví na výpočtu, přesněji řečeno na jeho výsledcích (interně se používá Go typ int):
user=> (defn factorial [n] (reduce * (range 1 (inc n)))) #'user/factorial
Výpočet faktoriálu si můžeme ověřit například tak, že se pokusíme vypočítat výsledky pro hodnoty 0! až 29!. V oboru celých čísel ovšem výsledky nevypadají příliš korektně:
user=> (doseq [n (range 0 30)] (println n (factorial n))) 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 21 -4249290049419214848 22 -1250660718674968576 23 8128291617894825984 24 -7835185981329244160 25 7034535277573963776 26 -1569523520172457984 27 -5483646897237262336 28 -5968160532966932480 29 -7055958792655077376
Lepšího výsledku dosáhneme při použití čísel s plovoucí řádovou čárkou (interně typ float64):
user> (defn factorial [n] (reduce * (range 1.0 (inc n)))) #'user/factorial user> (doseq [n (range 0 30)] (println n (factorial n))) 0 1 1 1 2 2 3 6 4 24 5 120 6 720 7 5040 8 40320 9 362880 10 3.6288e+06 11 3.99168e+07 12 4.790016e+08 13 6.2270208e+09 14 8.71782912e+10 15 1.307674368e+12 16 2.0922789888e+13 17 3.55687428096e+14 18 6.402373705728e+15 19 1.21645100408832e+17 20 2.43290200817664e+18 21 5.109094217170944e+19 22 1.1240007277776077e+21 23 2.585201673888498e+22 24 6.204484017332394e+23 25 1.5511210043330986e+25 26 4.0329146112660565e+26 27 1.0888869450418352e+28 28 3.0488834461171384e+29 29 8.841761993739701e+30
Tyto výsledky vypadají lépe, ovšem například 1000! přesáhne rozsah typu float64:
user> (factorial 1000) +Inf
Třetí varianta používá celá čísla s „nekonečným“ rozsahem:
user> (defn factorial [n] (reduce * (range 1N (inc n))))
Jedná se o nejlepší datový typ téměř přesně stvořený pro výpočet faktoriálů:
user> (doseq [n (range 0 30)] (println n (factorial n))) 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
Problém nenastane ani při výpočtu 1000!:
#'user/factorial user> (factorial 1000) 4023872600770937735437024339230039857193748642107146325437999 1042993851239862902059204420848696940480047998861019719605863 1666872994808558901323829669944590997424504087073759918823627 ... ... ... 2301353580818400969963725242305608559037006242712434169090041 5369010593398383577793941097002775347200000000000000000000000 0000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000N
14. Výpočet konstanty Pi Wallisovým součinem: použití reálných čísel a zlomků
Podívejme se nyní na použití různých datových typů na jednoduchém algoritmu určeném pro výpočet hodnoty čísla Pi s využitím takzvaného Wallisova součinu (Wallis product, viz též https://en.wikipedia.org/wiki/Wallis_product). Výpočet používající hodnoty s plovoucí řádovou čárkou může být implementován například takto (používáme zde programovou smyčku, i když by bylo možné algoritmus přepsat za pomoci rekurze – což je méně efektivní):
(defn compute-pi [n] (loop [pi 4.0 i 3] (if (< i (+ n 2)) (recur (* pi (/ (- i 1) i) (/ (+ i 1) i)) (+ i 2)) pi))) (loop [n 1] (print n "\t") (println (compute-pi n)) (if (< n 500000) (recur (* n 2))))
Výsledky budou sice vypočteny rychle, ovšem přesnost se od určitého bodu již nebude zvyšovat:
1 4.000000 2 3.555556 4 3.413333 8 3.302394 16 3.230036 32 3.188127 64 3.165482 128 3.153699 256 3.147687 512 3.144650 1024 3.143124 2048 3.142359 4096 3.141976 8192 3.141784 16384 3.141689 32768 3.141641 65536 3.141617 131072 3.141605 262144 3.141599 524288 3.141596
Druhá varianta používá čísla s teoreticky neomezenou přesností:
(defn compute-pi [n] (loop [pi 4M i 3] (if (< i (+ n 2)) (recur (* pi (/ (- i 1) i) (/ (+ i 1) i)) (+ i 2)) pi))) (loop [n 1] (print n "\t") (println (compute-pi n)) (if (< n 500000) (recur (* n 2))))
Výpočet je již znatelně pomalejší, ovšem Pi lze (s dostatkem času a paměti) vypočítat s libovolnou přesností, i když používáme jeden z nejpomaleji konvergujících algoritmů:
1 4M 2 3.5555555555555551608095912443887965835546460817611862740078511498087454612005M 4 3.41333333333333301753656188439991388456296898300406018291771319944995746157833M 8 3.30239355001259692280920194400154772374586363706317216774086896671698557673493M 16 3.23003646641171644532889395147155628591824505919549219334948928591485866419726M 32 3.1881271694471385210138122971756460360357166286716599658017344451317225348676M 64 3.16548206003479301000359223272779662079847690689602323017497171003960000199875M 128 3.15369884909579662233696379914525164935458647351778549138013266158467482689623M 256 3.14768689955642097012774122086854615055254871426894364462136545058514574740573M 512 3.1446501625172043342721016716151822642187838865539328374058640031469372655216M 1024 3.14312401702818716513564658579506676971691498322295763444751506627862492954596M 2048 3.14235898912177474909453737557243618376555954108506806271913844464157411813212M 4096 3.14197598500560203352568445241891251993592823431462623506515693992611545990133M 8192 3.14178436023473709091057936359290673473145653612318588673863895561787882156406M 16384 3.14168851714957705626885770501460708755455187703107092416262443647651781577763M 32768 3.1416405879293422812335507015910306209939040223297184216375211890918980802193M 65536 3.14161662139946575975135338281062749093860415881901622734854996519879152066546M 131072 3.1416046376545461304633895559846886819121577906409706156493279815083072754397M 262144 3.14159864566195543783459196307309124809158749182342203241630155983916009242113M 524288 3.14159564963570920196181065052181394692878789940139554703274845924206554511736M
Použít můžeme i zlomky, které jsou na konci převáděny na reálná čísla:
(defn compute-pi [n] (loop [pi 4/1 i 3] (if (< i (+ n 2)) (recur (* pi (/ (- i 1) i) (/ (+ i 1) i)) (+ i 2)) pi))) (loop [n 1] (print n "\t") (println (double (compute-pi n))) (if (< n 500000) (recur (* n 2))))
Výpočet je v tomto případě nejpomalejší:
1 4.000000 2 3.555556 4 3.413333 8 3.302394 16 3.230036 32 3.188127 64 3.165482 128 3.153699 256 3.147687 512 3.144650 1024 3.143124 2048 3.142359 4096 3.141976 8192 3.141784 16384 3.141689
15. Makro dotimes
V některých programech může být poměrně užitečné makro nazvané jednoduše a přitom příhodně dotimes, které dokáže nějaký výraz (formu) opakovat n krát. Přitom toto makro může v každé iteraci (opakování) nastavit zvolenou lokální proměnnou na aktuální hodnotu počitadla, přičemž se hodnota počitadla v první iteraci vždy nastavuje na nulu a v poslední iteraci dosahuje zadaného počtu opakování-1. Vzdáleně tedy můžeme toto makro považovat za ekvivalent programové smyčky for i in range(n): v programovacím jazyku Python či ekvivalent k počítané smyčce for (int i = 0; i<n; i++) známé z céčka (zde bez možnosti mít lokální proměnnou jako počitadlo), C++, Javy atd. Vzhledem k tomu, že se předpokládá, že forma – tělo smyčky – předaná makru dotimes bude mít nějaký vedlejší efekt, nejedná se sice o čistě funkcionální přístup, nicméně makro dotimes může být skutečně velmi užitečné.
V jednoduchém demonstračním příkladu, který si ukážeme, se na standardní výstup vypisuje převrácená hodnota celých čísel od 0 do 9. Vedlejším efektem je v tomto případě samotný výpis na standardní výstup:
user=> (dotimes [i 10] (println (/ 1.0 i))) +Inf 1.000000 0.500000 0.333333 0.250000 0.200000 0.166667 0.142857 0.125000 0.111111 nil
Poznámka: poslední vypsané nil je návratovou hodnotou samotného makra dotimes, nikoli výsledek poslední iterace)
Podívejme se nyní na poněkud složitější příklad, který by se v imperativních programovacích jazycích většinou řešil s využitím dvojice do sebe vnořených počítaných programových smyček. Mějme za úkol vypsat tabulku malé násobilky, tj. všechny výsledky vzniklé vynásobením dvojic celých čísel od 1 do 10. Tento algoritmus je možné velmi snadno realizovat právě s využitím makra dotimes, například následujícím one-linerem:
(dotimes [i 10] (dotimes [j 10] (print (* (+ i 1) (+ j 1)) "\t")) (println))
Pro větší přehlednost si můžeme výše uvedený one-liner přepsat na správně odsazený program, z něhož je patrné, že se skutečně jedná o ekvivalent dvou do sebe zanořených programových smyček:
(dotimes [i 10] (dotimes [j 10] (print (* (inc i) (inc j)) "\t")) (println))
A zde je již výsledek práce tohoto programu (poslední nil je opět návratovou hodnotou makra dotimes):
1 2 3 4 5 6 7 8 9 10 2 4 6 8 10 12 14 16 18 20 3 6 9 12 15 18 21 24 27 30 4 8 12 16 20 24 28 32 36 40 5 10 15 20 25 30 35 40 45 50 6 12 18 24 30 36 42 48 54 60 7 14 21 28 35 42 49 56 63 70 8 16 24 32 40 48 56 64 72 80 9 18 27 36 45 54 63 72 81 90 10 20 30 40 50 60 70 80 90 100 nil
16. Přímá rekurze
Všechny funkcionální jazyky podporují přímou rekurzi, takže se podívejme na její zápis. Typický školní příklad pro výpočet faktoriálu můžeme zapsat následovně:
(defn fact [n] (if (<= n 1) 1 (* n (fact (- n 1)))))
Vcelku snadno však může nastat situace, kdy se zcela zaplní paměť s návratovými body i parametry rekurzivně volané funkce:
user> (fact 1000000000) runtime: goroutine stack exceeds 1000000000-byte limit fatal error: stack overflow
V takovém případě celý interpret zhavaruje.
17. Tail rekurze
Důvod, proč předchozí volání funkce fact skončilo s chybou, spočívá v tom, že došlo k přeplnění zásobníku při rekurzivním volání. Na zásobník se totiž musí ukládat parametry předávané volané funkci a taktéž body návratu (zjednodušeně řečeno návratové adresy). Aby k přetečení zásobníku nedocházelo, můžeme naši funkci fact upravit tak, aby se využívalo takzvané tail rekurze. Velmi zjednodušeně řečeno je tail rekurze použita tehdy, pokud je posledním příkazem nějaké funkce příkaz pro rekurzivní volání té samé funkce. V tomto případě se nemusí na zásobník nic ukládat a namísto toho se prostě provede skok. V Jokeru se však musí tail rekurze zapsat explicitně, což má své přednosti i zápory (podle mě převažují přednosti, protože již ze zápisu programu je zcela zřejmé, kdy k tail rekurzi skutečně dojde).
Explicitní zápis rekurze spočívá ve využití speciální formy recur, která se zapíše přesně do místa, kde má k tail rekurzi (=skoku) dojít:
(defn fact ([n] (fact n 1)) ([n acc] (if (<= n 1) acc (recur (dec n) (* acc n)))))
18. Repositář s demonstračními příklady
Zdrojové kódy všech dnes použitých demonstračních příkladů byly uloženy do Git repositáře, který je dostupný na adrese https://github.com/tisnik/lisp-families.git (stále na GitHubu :-). V případě, že nebudete chtít klonovat celý repositář (ten je ovšem – alespoň prozatím – velmi malý, můžete namísto toho použít odkazy na jednotlivé příklady, které naleznete v následující tabulce:
19. Literatura
- Peter Seibel
„Practical Common Lisp“
2009 - Paul Graham
„ANSI Common Lisp“
1995 - Gerald Gazdar
„Natural Language Processing in Lisp: An Introduction to Computational Linguistics“
1989 - Peter Norvig
„Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp“
1991 - Alex Mileler et.al.
„Clojure Applied: From Practice to Practitioner“
2015 - „Living Clojure: An Introduction and Training Plan for Developers“
2015 - Dmitri Sotnikov
„Web Development with Clojure: Build Bulletproof Web Apps with Less Code“
2016 - McCarthy
„Recursive functions of symbolic expressions and their computation by machine, part I“
1960 - R. Kent Dybvig
„The Scheme Programming Language“
2009 - Max Hailperin
„Concrete Abstractions“
1998 - Guy L. Steele
„History of Scheme“
2006, Sun Microsystems Laboratories - Kolář J., Muller K.:
„Speciální programovací jazyky“
Praha 1981 - „AutoLISP Release 9, Programmer's reference“
Autodesk Ltd., October 1987 - „AutoLISP Release 10, Programmer's reference“
Autodesk Ltd., September 1988 - McCarthy, John; Abrahams, Paul W.; Edwards, Daniel J.; Hart, Timothy P.; Levin, Michael I.
„LISP 1.5 Programmer's Manual“
MIT Press. ISBN 0 262 130 1 1 4 - Carl Hewitt; Peter Bishop and Richard Steiger
„A Universal Modular Actor Formalism for Artificial Intelligence“
1973 - Feiman, J.
„The Gartner Programming Language Survey (October 2001)“
Gartner Advisory - Harold Abelson, Gerald Jay Sussman, Julie Sussman:
Structure and Interpretation of Computer Programs
MIT Press. 1985, 1996 (a možná vyšel i další přetisk) - Paul Graham
On Lisp
Prentice Hall, 1993
Dostupné online na stránce http://www.paulgraham.com/onlisptext.html - David S. Touretzky
Common LISP: A Gentle Introduction to Symbolic Computation (Dover Books on Engineering)
- Peter Norvig
Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp - Patrick Winston, Berthold Horn
Lisp (3rd Edition)
ISBN-13: 978–0201083194, ISBN-10: 0201083191 - Matthias Felleisen, David Van Horn, Dr. Conrad Barski
Realm of Racket: Learn to Program, One Game at a Time!
ISBN-13: 978–1593274917, ISBN-10: 1593274912 - Graham Hutton
A tutorial on the universality andexpressiveness of fold
http://www.cs.nott.ac.uk/~pszgmh/fold.pdf
20. Odkazy na Internetu
- Stránka projektu Joker
https://joker-lang.org/ - Racket: programovací jazyk a současně i platforma pro vývoj nových jazyků
https://www.root.cz/clanky/racket-programovaci-jazyk-a-soucasne-i-platforma-pro-vyvoj-novych-jazyku/ - Makra v Racketu i v dalších lispovských jazycích
https://www.root.cz/clanky/makra-v-racketu-i-v-dalsich-lispovskych-jazycich/ - Základní knihovna jazyka Racket
https://www.root.cz/clanky/zakladni-knihovna-jazyka-racket/ - Základní knihovny pro 2D grafiku v jazyku Racket
https://www.root.cz/clanky/zakladni-knihovny-pro-2d-grafiku-v-jazyku-racket/ - Popis formátu EDN
https://github.com/edn-format/edn - Jazyky Hy a Clojure-py: moderní dialekty LISPu určené pro Python VM
https://www.root.cz/clanky/jazyky-hy-a-clojure-py-moderni-dialekty-lispu-urcene-pro-python-vm/ - Grafický metaformát PostScript
https://www.root.cz/clanky/graficky-metaformat-postscript/ - Vektorový grafický formát SVG
https://www.root.cz/clanky/vektorovy-graficky-format-svg/ - The Racket Drawing Toolkit
https://docs.racket-lang.org/draw/index.html - Traditional Turtles
https://docs.racket-lang.org/turtles/Traditional_Turtles.html - [racket] How best to repeat a function call n times?
https://lists.racket-lang.org/users/archive/2014-September/064203.html - Racket: Macros
https://www.it.uu.se/edu/course/homepage/avfunpro/ht13/lectures/Racket-3-Macros.pdf - Beautiful Racket / explainers: Macros
https://beautifulracket.com/explainer/macros.html - Macros (dokumentace k Racketu)
https://docs.racket-lang.org/guide/macros.html - Model syntaxe jazyka Racket
https://docs.racket-lang.org/reference/syntax-model.html - Syntax Objects
https://docs.racket-lang.org/guide/stx-obj.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 - Beautiful Racket: an introduction to language-oriented programming using Racket
https://beautifulracket.com/ - Stránky projektu Racket
https://racket-lang.org/ - Dokumentace k projektu Racket
https://docs.racket-lang.org/index.html - Seznam dostupných balíčků pro Racket
https://pkgs.racket-lang.org/ - Racket na Wikipedii
https://en.wikipedia.org/wiki/Racket_(programming_language) - Blogy o Racketu a navazujících technologiích
https://blog.racket-lang.org/ - Prográmky psané v Racketu na RosettaCode
http://rosettacode.org/wiki/Category:Racket - Fear of Macros
https://www.greghendershott.com/fear-of-macros/ - Rackjure
https://github.com/greghendershott/rackjure - Matthew Flatt’s proposal to change Racket’s s-expressions based syntax to infix representation creates a stir in the community
https://hub.packtpub.com/matthew-flatts-proposal-to-change-rackets-s-expressions-based-syntax-to-infix-representation-creates-a-stir-in-the-community/ - Racket News
https://racket-news.com/ - Racket: Lisp for learning
https://lwn.net/Articles/795385/ - Future of Racket
https://www.greghendershott.com/2019/07/future-of-racket.html - Kawa: Compiling Scheme to Java
https://www.mit.edu/afs.new/sipb/project/kawa/doc/kawa-tour.html - Kawa in Languages shootout
http://per.bothner.com/blog/2010/Kawa-in-shootout/ - Kawa 2.0 Supports Scheme R7RS
https://developers.slashdot.org/story/14/12/13/2259225/kawa-20-supports-scheme-r7rs/ - Kawa — fast scripting on the Java platform
https://lwn.net/Articles/623349/ - Tail call (a její optimalizace)
https://en.wikipedia.org/wiki/Tail_call - SLIME (Wikipedia)
http://en.wikipedia.org/wiki/SLIME - slime.vim
http://s3.amazonaws.com/mps/slime.vim - What are the best scheme implementations?
https://www.slant.co/topics/5282/~scheme-implementations - Bigloo homepage
http://www-sop.inria.fr/mimosa/fp/Bigloo/ - FTP s tarbally Bigloo
ftp://ftp-sop.inria.fr/indes/fp/Bigloo - GOTO 2018 • Functional Programming in 40 Minutes • Russ Olsen
https://www.youtube.com/watch?v=0if71HOyVjY - TinyScheme (stránka na Sourceforge)
http://tinyscheme.sourceforge.net/home.html - Embedding Tiny Scheme in a Game
http://www.silicondelight.com/embedding-tiny-scheme-in-a-game/ - Embedding Scheme for a game mission scripting DSL
http://carloscarrasco.com/embedding-scheme-for-a-game-mission-scripting-dsl.html - Všechny verze TinyScheme na SourceForge
https://sourceforge.net/projects/tinyscheme/files/tinyscheme/ - Fork TinyScheme na GitHubu
https://github.com/yawnt/tinyscheme - Ackermannova funkce
https://cs.wikipedia.org/wiki/Ackermannova_funkce - Ackermann function na Rosetta Code
https://rosettacode.org/wiki/Ackermann_function#Scheme - Success Stories (lisp.org)
https://lisp-lang.org/success/ - Allegro Common Lisp Success Stories
https://franz.com/success/ - Clojure Success Stories
https://clojure.org/community/success_stories - Scheme Quick Reference
https://www.st.cs.uni-saarland.de/edu/config-ss04/scheme-quickref.pdf - Slajdy o Scheme (od slajdu číslo 15)
https://docs.google.com/presentation/d/1abmDnKjrq1tcjGvvRNAKhOiSTSE2lyagtcEPal07Gbo/edit - Scheme Cheat Sheet
https://github.com/smythp/scheme-cheat-sheet - Embedding Lua, embedding Guile
http://puntoblogspot.blogspot.com/2013/04/embedding-lua-embedding-guile.html - Lambda Papers
https://en.wikisource.org/wiki/Lambda_Papers - Revised7Report on the Algorithmic Language Scheme
https://small.r7rs.org/attachment/r7rs.pdf - Video Lectures (MIT, SICP 2005)
https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6–001-structure-and-interpretation-of-computer-programs-spring-2005/video-lectures/ - Why is Scheme my first language in university?
https://softwareengineering.stackexchange.com/questions/115252/why-is-scheme-my-first-language-in-university - The Perils of JavaSchools
https://www.joelonsoftware.com/2005/12/29/the-perils-of-javaschools-2/ - How to Design Programs, Second Edition
https://htdp.org/2019–02–24/index.html - LilyPond
http://lilypond.org/ - LilyPond — Extending (přes Scheme)
http://lilypond.org/doc/v2.18/Documentation/extending/scheme-tutorial - Scheme in LilyPond
http://lilypond.org/doc/v2.18/Documentation/extending/scheme-in-lilypond - GnuCash
http://www.gnucash.org/ - Custom Reports (in GNU Cash)
https://wiki.gnucash.org/wiki/Custom_Reports - Program by Design
https://programbydesign.org/ - SchemePy
https://pypi.org/project/SchemePy/ - LISP FQA: Section – [1–5] What is the „minimal“ set of primitives needed for a Lisp interpreter?
http://www.faqs.org/faqs/lisp-faq/part1/section-6.html - femtolisp
https://github.com/JeffBezanson/femtolisp - (How to Write a (Lisp) Interpreter (in Python))
http://norvig.com/lispy.html - Repositář s Guile Emacsem
http://git.hcoop.net/?p=bpt/guile.git - Interacting with Guile Compound Data Types in C
http://www.lonelycactus.com/guilebook/x1555.html - Calling Guile functions from C
http://www.lonelycactus.com/guilebook/c1204.html#SECCALLGUILEFUNC - Arrays, and other compound data types
http://www.lonelycactus.com/guilebook/charrays.html - Interacting with Guile Compound Data Types in C
http://www.lonelycactus.com/guilebook/x1555.html - Guile Reference Manual
https://www.gnu.org/software/guile/manual/html_node/index.html - Scheme: Summary of Common Syntax
https://www.gnu.org/software/guile/manual/html_node/Syntax-Summary.html#Syntax-Summary - Scripting with Guile: Extension language enhances C and Scheme
https://www.ibm.com/developerworks/library/l-guile/index.html - Having fun with Guile: a tutorial
http://dustycloud.org/misc/guile-tutorial.html - Guile: Loading Readline Support
https://www.gnu.org/software/guile/manual/html_node/Loading-Readline-Support.html#Loading-Readline-Support - lispy
https://pypi.org/project/lispy/ - Lython
https://pypi.org/project/Lython/ - Lizpop
https://pypi.org/project/lizpop/ - Budoucnost programovacích jazyků
http://www.knesl.com/budoucnost-programovacich-jazyku - LISP Prolog and Evolution
http://blog.samibadawi.com/2013/05/lisp-prolog-and-evolution.html - List of Lisp-family programming languages
https://en.wikipedia.org/wiki/List_of_Lisp-family_programming_languages - clojure_py na indexu PyPi
https://pypi.python.org/pypi/clojure_py - PyClojure
https://github.com/eigenhombre/PyClojure - Hy na GitHubu
https://github.com/hylang/hy - Hy: The survival guide
https://notes.pault.ag/hy-survival-guide/ - Hy běžící na monitoru terminálu společnosti Symbolics
http://try-hy.appspot.com/ - Welcome to Hy’s documentation!
http://docs.hylang.org/en/stable/ - Hy na PyPi
https://pypi.org/project/hy/#description - Getting Hy on Python
https://lwn.net/Articles/596626/ - Programming Can Be Fun with Hy
https://opensourceforu.com/2014/02/programming-can-fun-hy/ - Přednáška o projektu Hy (pětiminutový lighttalk)
http://blog.pault.ag/day/2013/04/02 - Hy (Wikipedia)
https://en.wikipedia.org/wiki/Hy - GNU Emacs Lisp Reference Manual: Point
https://www.gnu.org/software/emacs/manual/html_node/elisp/Point.html - GNU Emacs Lisp Reference Manual: Narrowing
https://www.gnu.org/software/emacs/manual/html_node/elisp/Narrowing.html - GNU Emacs Lisp Reference Manual: Functions that Create Markers
https://www.gnu.org/software/emacs/manual/html_node/elisp/Creating-Markers.html - GNU Emacs Lisp Reference Manual: Motion
https://www.gnu.org/software/emacs/manual/html_node/elisp/Motion.html#Motion - GNU Emacs Lisp Reference Manual: Basic Char Syntax
https://www.gnu.org/software/emacs/manual/html_node/elisp/Basic-Char-Syntax.html - Elisp: Sequence: List, Array
http://ergoemacs.org/emacs/elisp_list_vs_vector.html - Elisp: Property List
http://ergoemacs.org/emacs/elisp_property_list.html - Elisp: Hash Table
http://ergoemacs.org/emacs/elisp_hash_table.html - Elisp: Association List
http://ergoemacs.org/emacs/elisp_association_list.html - The mapcar Function (An Introduction to Programming in Emacs Lisp)
https://www.gnu.org/software/emacs/manual/html_node/eintr/mapcar.html - Anaphoric macro
https://en.wikipedia.org/wiki/Anaphoric_macro - Some Common Lisp Loop Macro Examples
https://www.youtube.com/watch?v=3yl8o6r_omw - A Guided Tour of Emacs
https://www.gnu.org/software/emacs/tour/ - The Roots of Lisp
http://www.paulgraham.com/rootsoflisp.html - Evil (Emacs Wiki)
https://www.emacswiki.org/emacs/Evil - Evil (na GitHubu)
https://github.com/emacs-evil/evil - Evil (na stránkách repositáře MELPA)
https://melpa.org/#/evil - Evil Mode: How I Switched From VIM to Emacs
https://blog.jakuba.net/2014/06/23/evil-mode-how-to-switch-from-vim-to-emacs.html - GNU Emacs (home page)
https://www.gnu.org/software/emacs/ - GNU Emacs (texteditors.org)
http://texteditors.org/cgi-bin/wiki.pl?GnuEmacs - An Introduction To Using GDB Under Emacs
http://tedlab.mit.edu/~dr/gdbintro.html - An Introduction to Programming in Emacs Lisp
https://www.gnu.org/software/emacs/manual/html_node/eintr/index.html - 27.6 Running Debuggers Under Emacs
https://www.gnu.org/software/emacs/manual/html_node/emacs/Debuggers.html - GdbMode
http://www.emacswiki.org/emacs/GdbMode - Emacs (Wikipedia)
https://en.wikipedia.org/wiki/Emacs - Emacs timeline
http://www.jwz.org/doc/emacs-timeline.html - Emacs Text Editors Family
http://texteditors.org/cgi-bin/wiki.pl?EmacsFamily - Vrapper aneb spojení možností Vimu a Eclipse
https://mojefedora.cz/vrapper-aneb-spojeni-moznosti-vimu-a-eclipse/ - Vrapper aneb spojení možností Vimu a Eclipse (část 2: vyhledávání a nahrazování textu)
https://mojefedora.cz/vrapper-aneb-spojeni-moznosti-vimu-a-eclipse-cast-2-vyhledavani-a-nahrazovani-textu/ - Emacs/Evil-mode – A basic reference to using evil mode in Emacs
http://www.aakarshnair.com/posts/emacs-evil-mode-cheatsheet - From Vim to Emacs+Evil chaotic migration guide
https://juanjoalvarez.net/es/detail/2014/sep/19/vim-emacsevil-chaotic-migration-guide/ - Introduction to evil-mode {video)
https://www.youtube.com/watch?v=PeVQwYUxYEg - EINE (Emacs Wiki)
http://www.emacswiki.org/emacs/EINE - EINE (Texteditors.org)
http://texteditors.org/cgi-bin/wiki.pl?EINE - ZWEI (Emacs Wiki)
http://www.emacswiki.org/emacs/ZWEI - ZWEI (Texteditors.org)
http://texteditors.org/cgi-bin/wiki.pl?ZWEI - Zmacs (Wikipedia)
https://en.wikipedia.org/wiki/Zmacs - Zmacs (Texteditors.org)
http://texteditors.org/cgi-bin/wiki.pl?Zmacs - TecoEmacs (Emacs Wiki)
http://www.emacswiki.org/emacs/TecoEmacs - Micro Emacs
http://www.emacswiki.org/emacs/MicroEmacs - Micro Emacs (Wikipedia)
https://en.wikipedia.org/wiki/MicroEMACS - EmacsHistory
http://www.emacswiki.org/emacs/EmacsHistory - Seznam editorů s ovládáním podobným Emacsu či kompatibilních s příkazy Emacsu
http://www.finseth.com/emacs.html - evil-numbers
https://github.com/cofi/evil-numbers - Debuggery a jejich nadstavby v Linuxu (1.část)
http://fedora.cz/debuggery-a-jejich-nadstavby-v-linuxu/ - Debuggery a jejich nadstavby v Linuxu (2.část)
http://fedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-2-cast/ - Debuggery a jejich nadstavby v Linuxu (3): Nemiver
http://fedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-3-nemiver/ - Debuggery a jejich nadstavby v Linuxu (4): KDbg
http://fedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-4-kdbg/ - Debuggery a jejich nadstavby v Linuxu (5): ladění aplikací v editorech Emacs a Vim
https://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-5-ladeni-aplikaci-v-editorech-emacs-a-vim/ - Org mode
https://orgmode.org/ - The Org Manual
https://orgmode.org/manual/index.html - Kakoune (modální textový editor)
http://kakoune.org/ - Vim-style keybinding in Emacs/Evil-mode
https://gist.github.com/troyp/6b4c9e1c8670200c04c16036805773d8 - Emacs – jak začít
http://www.abclinuxu.cz/clanky/navody/emacs-jak-zacit - Programovací jazyk LISP a LISP machines
https://www.root.cz/clanky/programovaci-jazyk-lisp-a-lisp-machines/ - Evil-surround
https://github.com/emacs-evil/evil-surround - Spacemacs
http://spacemacs.org/ - Lisp: Common Lisp, Racket, Clojure, Emacs Lisp
http://hyperpolyglot.org/lisp - Common Lisp, Scheme, Clojure, And Elisp Compared
http://irreal.org/blog/?p=725 - Does Elisp Suck?
http://irreal.org/blog/?p=675 - Emacs pro mírně pokročilé (9): Elisp
https://www.root.cz/clanky/emacs-elisp/ - If I want to learn lisp, are emacs and elisp a good choice?
https://www.reddit.com/r/emacs/comments/2m141y/if_i_want_to_learn_lisp_are_emacs_and_elisp_a/ - Clojure(Script) Interactive Development Environment that Rocks!
https://github.com/clojure-emacs/cider - An Introduction to Emacs Lisp
https://harryrschwartz.com/2014/04/08/an-introduction-to-emacs-lisp.html - Emergency Elisp
http://steve-yegge.blogspot.com/2008/01/emergency-elisp.html - Lambda calculus
https://en.wikipedia.org/wiki/Lambda_calculus - John McCarthy's original LISP paper from 1959
https://www.reddit.com/r/programming/comments/17lpz4/john_mccarthys_original_lisp_paper_from_1959/ - Micro Manual LISP
https://www.scribd.com/document/54050141/Micro-Manual-LISP - How Lisp Became God's Own Programming Language
https://twobithistory.org/2018/10/14/lisp.html - History of Lisp
http://jmc.stanford.edu/articles/lisp/lisp.pdf - The Roots of Lisp
http://languagelog.ldc.upenn.edu/myl/llog/jmc.pdf - Racket
https://racket-lang.org/ - The Racket Manifesto
http://felleisen.org/matthias/manifesto/ - MIT replaces Scheme with Python
https://www.johndcook.com/blog/2009/03/26/mit-replaces-scheme-with-python/ - Adventures in Advanced Symbolic Programming
http://groups.csail.mit.edu/mac/users/gjs/6.945/ - Why MIT Switched from Scheme to Python (2009)
https://news.ycombinator.com/item?id=14167453 - Starodávná stránka XLispu
http://www.xlisp.org/ - AutoLISP
https://en.wikipedia.org/wiki/AutoLISP - Seriál PicoLisp: minimalistický a výkonný interpret Lispu
https://www.root.cz/serialy/picolisp-minimalisticky-a-vykonny-interpret-lispu/ - Common Lisp
https://common-lisp.net/ - Getting Going with Common Lisp
https://cliki.net/Getting%20Started - Online Tutorial (Common Lisp)
https://cliki.net/online%20tutorial - Guile Emacs
https://www.emacswiki.org/emacs/GuileEmacs - Guile Emacs History
https://www.emacswiki.org/emacs/GuileEmacsHistory - Guile is a programming language
https://www.gnu.org/software/guile/ - MIT Scheme
http://groups.csail.mit.edu/mac/projects/scheme/ - SIOD: Scheme in One Defun
http://people.delphiforums.com/gjc//siod.html - CommonLispForEmacs
https://www.emacswiki.org/emacs/CommonLispForEmacs - Elisp: print, princ, prin1, format, message
http://ergoemacs.org/emacs/elisp_printing.html - Special Forms in Lisp
http://www.nhplace.com/kent/Papers/Special-Forms.html - Basic Building Blocks in LISP
https://www.tutorialspoint.com/lisp/lisp_basic_syntax.htm - Introduction to LISP – University of Pittsburgh
https://people.cs.pitt.edu/~milos/courses/cs2740/Lectures/LispTutorial.pdf - Why don't people use LISP
https://forums.freebsd.org/threads/why-dont-people-use-lisp.24572/ - Structured program theorem
https://en.wikipedia.org/wiki/Structured_program_theorem - Clojure: API Documentation
https://clojure.org/api/api - Tutorial for the Common Lisp Loop Macro
http://www.ai.sri.com/pkarp/loop.html - Common Lisp's Loop Macro Examples for Beginners
http://www.unixuser.org/~euske/doc/cl/loop.html - A modern list api for Emacs. No 'cl required.
https://github.com/magnars/dash.el - The LOOP Facility
http://www.lispworks.com/documentation/HyperSpec/Body/06_a.htm - 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 Structureshttp://clojure.org/transients
- 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 - 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í - 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/ - Stránka projektu Jython
http://www.jython.org/ - Jython (Wikipedia)
https://en.wikipedia.org/wiki/Jython - Scripting for the Java Platform (Wikipedia)
https://en.wikipedia.org/wiki/Scripting_for_the_Java_Platform - JSR 223: Scripting for the JavaTM Platform
https://jcp.org/en/jsr/detail?id=223 - List of JVM languages
https://en.wikipedia.org/wiki/List_of_JVM_languages - The JavaTM Virtual Machine Specification, Second Edition
http://java.sun.com/docs/books/jvms/second_edition/html/VMSpecTOC.doc.html - The class File Format
http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html - javap – The Java Class File Disassembler
http://docs.oracle.com/javase/1.4.2/docs/tooldocs/windows/javap.html - javap-java-1.6.0-openjdk(1) – Linux man page
http://linux.die.net/man/1/javap-java-1.6.0-openjdk - Using javap
http://www.idevelopment.info/data/Programming/java/miscellaneous_java/Using_javap.html - Examine class files with the javap command
http://www.techrepublic.com/article/examine-class-files-with-the-javap-command/5815354