Obsah
1. Projekt Gambit – další kvalitní interpret i překladač programovacího jazyka Scheme
2. Transpřekladače; interaktivní smyčka REPL
5. Interpret a překladač – gsi a gsc
6. Použití interpretru dodávaného současně s Gambitem
7. Debugger, který je součástí REPLu
8. Základní vlastnosti jazyka Scheme
9. Rozšíření zavedená (nejenom) systémem Gambit – nepovinné parametry funkcí
10. Další možnosti volání funkcí
11. Definice nových datových typů (struktur)
12. Příprava jednoduchého benchmarku
13. Porovnání rychlosti interpretrů Gambit, GNU Guile a Chicken Scheme
14. Rychlost benchmarku přeloženého do nativního kódu
15. Přepis algoritmu takovým způsobem, aby používal výpočty s hodnotami s plovoucí řádovou čárkou
16. Použití vláken, paralelizace výpočtů
17. Porovnání všech benchmarků
18. Repositář s demonstračními příklady
1. Projekt Gambit – další kvalitní interpret i překladač programovacího jazyka Scheme
„Scheme is simple, weird, and cool.“
„My God, It's full of 'car's“
Jak jsme se již zmínili v perexu dnešního článku, existuje v současnosti přibližně padesát reálně použitelných implementací programovacího jazyka Scheme, ať již se jedná o interpretry či o překladače. Pokud však budeme hledat skutečně kvalitní a stabilní implementaci vhodnou pro produkční nasazení (a i když se to může zdát divné, Scheme skutečně bývá takto nasazováno, i když se z různých důvodů jedná o málo známá řešení), zmenšuje se počet vhodných systémů postavených nad jazykem Scheme na zhruba patnáct, přičemž jednotlivé vhodné projekty můžeme podle použitých technologií rozdělit do několika skupin:
- V první skupině nalezneme klasické interpretry běžící nad nějakým virtuálním strojem, popř. překladače do bajtkódu těchto virtuálních strojů. Do této kategorie patří například Gauche, Guile, systém Kawa, Scheme48, SISC (Second Interpreter of Scheme), SCM či Ypsilon (ten se používá pro programování pravidel pinballů, resp. video verzí těchto her). Některé ze zmíněných implementací Scheme používají vlastní virtuální stroj (Guile), další pak nějakou již existující variantu virtuálního stroje (Kawa, SISC).
- Ve druhé skupině, která je relativně rozsáhlá, nalezneme překladače programovacího jazyka Scheme do nativního (strojového) kódu. Do této kategorie můžeme zařadit například Chez Scheme (ten získal nejlepší hostname), Ikarus, Larceny, MIT Scheme, MzScheme či již popsaný rozsáhlý systém Racket založený na MzScheme, který je mj. používán i pro výuku (a to mj. i proto, že obsahuje vlastní GUI, podporu pro tvorbu grafů, interpretry dalších jazyků apod.).
- A konečně existuje i skupina implementací programovacího jazyka Scheme založená na transpřekladači (transcompileru, transpileru), typicky s výstupem do programovacího jazyka C. To znamená, že se vstupní kód napsaný v jazyce Scheme analyzuje, transformuje a optimalizuje, ovšem výstupem není přímo strojový kód, ale více či méně čitelný kód naprogramovaný v jazyku C (a teoreticky samozřejmě i do jiného jazyka, podle mě by byl v této roli ideální jazyk Rust). Do této skupiny řadíme především čtveřici Bigloo, Chicken, Gambit-C a Stalin. Ovšem transpřekladačem může být vybavena i implementace SISC zmíněná v první skupině (tento transpřekladač se jmenuje Hobbit).
V dnešním článku se zaměříme na třetí zmíněnou skupinu, konkrétně na projekt Gambit-C či zkráceně jen Gambit. Tento projekt obsahuje jak již zmíněný transpřekladač, tak i interpret doplněný o interaktivní programovou smyčku REPL neboli Read-Eval-Print-Loop (REPL je podle mého názoru jedním z nejdůležitějších vynálezů v oblasti IT – zdaleka se totiž nemusí jednat o pouhou interaktivní příkazovou řádku, což ostatně ukazuje popularita projektů založených na diářích, například Jupyter Notebooku).
2. Transpřekladače; interaktivní smyčka REPL
V úvodní kapitole jsme se mj. zmínili i o problematice takzvaných transpřekladačů (transcompilers, source-to-source compilers). Připomeňme si, že transpřekladače jsou (dosti obecně řečeno) nástroje sloužící pro překlad algoritmů zapsaných v nějakém zdrojovém programovacím jazyce do zvoleného cílového jazyka (ovšem nikoli do nativního kódu, mezikódu – intermetidate code – či bajtkódu, to je totiž role běžných překladačů).
Transpřekladače se v informatice používají již po několik desetiletí; například se stále můžeme setkat s nástroji, které převádí kód z nějakého vyššího programovacího jazyka do Céčka, které je dnes s trochou nadsázky chápáno jako „univerzální assembler“. Asi nejznámějším příkladem staršího použití transpřekladačů je nástroj nazvaný web2c, jenž slouží pro transformaci zdrojových kódů TeXu do céčka. Transpřekladače se stávají velmi populární i pro programátory webových aplikací, a to zejména z toho důvodu, že webové prohlížeče nativně podporují většinou pouze JavaScript, který je tak přirozeně cílovým jazykem transpřekladačů (proto se také JavaScriptu někdy říká „assembler pro web“).
Z praxe můžeme uvést například následující projekty založené na transpřekladači:
# | Jazyk či transpřekladač | Poznámka |
---|---|---|
1 | CoffeeScript | přidání syntaktického cukru do JavaScriptu |
2 | ClojureScript | překlad aplikací psaných v Clojure do JavaScriptu |
3 | TypeScript | nadmnožina jazyka JavaScript, přidání datových typů |
4 | 6to5 | transpřeklad z ECMAScript 6 (nová varianta JavaScriptu) do starší varianty JavaScriptu |
5 | Kaffeine | rozšíření JavaScriptu o nové vlastnosti |
6 | RedScript | jazyk inspirovaný Ruby |
7 | GorillaScript | další rozšíření JavaScriptu |
8 | ghcjs | transpřekladač pro fanoušky programovacího jazyka Haskell |
9 | Haxe | transpřekladač, mezi jehož cílové jazyka patří i Java a JavaScript |
10 | Wisp | transpřekladač jazyka podobného Clojure, opět do JavaScriptu |
11 | ScriptSharp | transpřekladač z C# do JavaScriptu |
12 | Dart | transpřekladač z jazyka Dart do JavaScriptu |
13 | COBOL → C | transpřekladač OpenCOBOL |
14 | COBOL → Java | transpřekladač P3COBOL |
15 | lua2js | transpřekladač jazyka Lua, opět do JavaScriptu |
16 | Coconut | transpřekladač jazyka Coconut do Pythonu |
I s principem a použitím interaktivní smyčky REPL jsme se již na stránkách Rootu setkali, a to dokonce mnohokrát. Kromě článků, které se věnovaly klasickým Unixovým shellům typu BASH, tcsh či zsh (a rozhraní shellů není nic jiného, než interaktivní REPL), jsme smyčku REPL použili například při popisu programovacího jazyka Julia či jazyka Clojure. Historií vzniku REPL jsme se zabývali i zde.
Některé smyčky REPL jsou pojaty přísně minimalisticky, což je případ dnes již spíše méně často používaného jazyka TCL. Tato interaktivní REPL dokonce ani neobsahuje historii příkazů či podporu pro pohyb kurzoru na příkazovém řádku:
%
Další interaktivní REPL alespoň uživatele informují, v jakém prostředí se nachází. To je případ REPL (opět velmi jednoduše pojaté) programovacího jazyka Lua:
Lua 5.2.3 Copyright (C) 1994-2013 Lua.org, PUC-Rio >
REPL projektu LuaJIT vypadá nepatrně odlišně:
LuaJIT 2.1.0-beta3 -- Copyright (C) 2005-2017 Mike Pall. http://luajit.org/ JIT: ON SSE2 SSE3 SSE4.1 BMI2 fold cse dce fwd dse narrow loop abc sink fuse >
Nově se plnohodnotný REPL objevil i v jazyku Clojure; v předchozích verzích se používal dále zmíněný alternativní nREPL:
Clojure 1.9.0 user=>
Podobně vypadá REPL programovacího jazyka Pixie (to vlastně není nijak překvapivé, protože se opět jedná o jednu z v:
user =>
Interaktivní rozhraní projektu GNU Guile, což je jedna z variant programovacího jazyka Scheme:
GNU Guile 2.0.14 Copyright (C) 1995-2016 Free Software Foundation, Inc. Guile comes with ABSOLUTELY NO WARRANTY; for details type `,show w'. This program is free software, and you are welcome to redistribute it under certain conditions; type `,show c' for details. Enter `,help' for help. scheme@(guile-user)>
Plnohodnotnou smyčku REPL se všemi vymoženostmi nabízí zejména programovací jazyk Julia, což ostatně není překvapivé, protože se tento jazyk používá právě pro postupnou inkrementální tvorbu aplikací v interaktivním prostředí:
_ _ _ _(_)_ | Documentation: https://docs.julialang.org (_) | (_) (_) | _ _ _| |_ __ _ | Type "?" for help, "]?" for Pkg help. | | | | | | |/ _` | | | | |_| | | | (_| | | Version 1.0.0 (2018-08-08) _/ |\__'_|_|_|\__'_| | Official https://julialang.org/ release |__/ | julia>
Podobně je tomu v případě programovacího jazyka Clojure doplněného o nREPL (ten lze spustit například příkazem lein repl):
nREPL server started on port 42733 on host 127.0.0.1 - nrepl://127.0.0.1:42733 REPL-y 0.3.7, nREPL 0.2.12 Clojure 1.8.0 OpenJDK 64-Bit Server VM 1.8.0_171-b10 Docs: (doc function-name-here) (find-doc "part-of-name-here") Source: (source function-name-here) Javadoc: (javadoc java-object-or-class-here) Exit: Control+D or (exit) or (quit) Results: Stored in vars *1, *2, *3, an exception in *e user=>
Samozřejmě nesmíme zapomenout ani na REPL doménově specifického jazyka R:
R version 3.5.3 (2019-03-11) -- "Great Truth" Copyright (C) 2019 The R Foundation for Statistical Computing Platform: x86_64-redhat-linux-gnu (64-bit) R is free software and comes with ABSOLUTELY NO WARRANTY. You are welcome to redistribute it under certain conditions. Type 'license()' or 'licence()' for distribution details. Natural language support but running in an English locale R is a collaborative project with many contributors. Type 'contributors()' for more information and 'citation()' on how to cite R or R packages in publications. Type 'demo()' for some demos, 'help()' for on-line help, or 'help.start()' for an HTML browser interface to help. Type 'q()' to quit R. During startup - Warning messages: 1: Setting LC_TIME failed, using "C" 2: Setting LC_MONETARY failed, using "C" 3: Setting LC_PAPER failed, using "C" 4: Setting LC_MEASUREMENT failed, using "C" gt;
Obrázek 1: Jedna z alternativních REPL určených pro programovací jazyk Python.
A v neposlední řadě nalezneme REPL i v systému Gambit, jemuž se věnujeme v dnešním článku:
$ gsi Gambit v4.8.8 >
Obrázek 2: Za zjednodušenou formu interaktivní smyčky REPL je možné považovat i takzvaný přímý mód (direct mode) použitý například v klasickém BASICu. Na tomto screenshotu je v přímém módu zapsán příkaz LIST.
3. Gambit se představuje
Projekt Gambit má za sebou poměrně velmi dlouhý vývoj, protože jeho existenci můžeme vysledovat až do roku 1989, tedy 31 let do minulosti. V té době vznikl první překladač (tedy nikoli ještě transpřekladač) jazyka Scheme generující strojový kód pro slavný mikroprocesor Motorola 68000 (M68k). Tehdy obsahoval Gambit skutečně pouze překladač, nikoli interpret. Dokonce, což je zvláštní, se v runtime zpočátku ani nenacházel systém pro automatickou správu paměti (garbage collector), což je pro LISPovský programovací jazyk spíše výjimka.
Obrázek 3: Na tomto grafu evoluce programovacích jazyků můžeme vidět některé historicky významné programovací jazyky, s nimiž jsme se již setkali v seriálu o historii počítačů. Jedná se zejména o Fortran, Cobol, SNOBOL, Algol, APL, BASIC (resp. přesněji řečeno celá rodina jazyků nesoucích toho jméno) a samozřejmě taktéž o LISP a jeho varianty.
Historie systému Gambit-C resp. později Gambit v kostce:
- 1989: první překladač pro mikroprocesor M68K, prozatím bez interprertru a bez GC
- 1991: MacGambit pro platformu Apple Macintosh
- 1994: první backend pro C, použití Gambitu v komerční sféře
- 2004: Gambit v4, podpora vláken, podporuje LGPL i Apache Licenci
Původním cílem Gambitu bylo zajistit kompatibilitu s R5RS. Dále bylo naplánováno vytvořit systém přenositelný na různé platformy s generátorem velmi rychlého a současně i malého výsledného binárního kódu. Dnes se s tímto systémem setkáme ve výzkumu, při převodu (transpilaci) Scheme do céčka (což si popíšeme dnes), JavaScriptu či do VHDL a Gambit je dostupný i pro embedded systémy (obraz může být menší než 20kB a stále kompatibilní alespoň s R4RS). Již méně často se Gambit používá jako skriptovací jazyk vkládaný do aplikací; to je dnes doménou GNU Guile.
4. Instalace projektu Gambit
Gambit je dostupný ve formě balíčku pro různé varianty Linuxu, takže mnohdy ani není nutné stahovat a překládat zdrojové kódy. Instalace na postarší (dnes již vlastně historické) Fedoře 27:
$ sudo dnf install gambit-c Last metadata expiration check: 0:09:50 ago on Sat 04 Jul 2020 08:05:09 PM CEST. Dependencies resolved. ================================================================================ Package Arch Version Repository Size ================================================================================ Installing: gambit-c x86_64 4.8.8-3.fc27 fedora 6.3 M Transaction Summary ================================================================================ Install 1 Package Total download size: 6.3 M Installed size: 41 M Is this ok [y/N]:
Průběh instalace:
Downloading Packages: gambit-c-4.8.8-3.fc27.x86_64.rpm 1.9 MB/s | 6.3 MB 00:03 -------------------------------------------------------------------------------- Total 1.3 MB/s | 6.3 MB 00:04 Running transaction check Transaction check succeeded. Running transaction test Transaction test succeeded. Running transaction Preparing : 1/1 Installing : gambit-c-4.8.8-3.fc27.x86_64 1/1 Running scriptlet: gambit-c-4.8.8-3.fc27.x86_64 1/1 Running as unit: run-r04b3ce47d8fc449e97c9e1ae9a102764.service Verifying : gambit-c-4.8.8-3.fc27.x86_64 1/1 Installed: gambit-c.x86_64 4.8.8-3.fc27 Complete!
Instalace na Fedoře 32 (včetně překladače C a linkeru):
$ sudo dnf install gambit-c Dependencies resolved. ================================================================================ Package Arch Version Repository Size ================================================================================ Installing: gambit-c x86_64 4.9.3-3.fc32 beaker-Fedora-Everything 6.0 M Installing dependencies: binutils x86_64 2.34-6.fc32 updates 5.4 M binutils-gold x86_64 2.34-6.fc32 updates 852 k cpp x86_64 10.2.1-6.fc32 updates 9.4 M gcc x86_64 10.2.1-6.fc32 updates 30 M glibc-devel x86_64 2.31-4.fc32 updates 1.0 M glibc-headers x86_64 2.31-4.fc32 updates 457 k isl x86_64 0.16.1-10.fc32 beaker-Fedora-Everything 872 k kernel-headers x86_64 5.9.13-100.fc32 updates 1.2 M libmpc x86_64 1.1.0-8.fc32 beaker-Fedora-Everything 59 k libxcrypt-devel x86_64 4.4.17-1.fc32 updates 32 k Transaction Summary ================================================================================ Install 11 Packages Total download size: 55 M Installed size: 191 M
Instalace na Linux Mintu (pozor na odlišné jméno balíčku):
$ sudo apt-get install install gambc Reading package lists... Done Building dependency tree Reading state information... Done The following additional packages will be installed: gambc-doc libgambit4 libgambit4-dev The following NEW packages will be installed: gambc gambc-doc libgambit4 libgambit4-dev 0 upgraded, 4 newly installed, 0 to remove and 399 not upgraded. Need to get 593 kB/3 101 kB of archives. After this operation, 21,7 MB of additional disk space will be used. Do you want to continue? [Y/n] y ading package lists... Done Setting up gambc-doc (4.8.8-3.1) ... Setting up libgambit4:amd64 (4.8.8-3.1) ... Setting up libgambit4-dev:amd64 (4.8.8-3.1) ... Setting up gambc (4.8.8-3.1) ... update-alternatives: using /usr/bin/gsi to provide /usr/bin/scheme-r5rs (scheme-r5rs) in auto mode Processing triggers for libc-bin (2.31-0ubuntu9) ... Processing triggers for man-db (2.9.1-1) ... Processing triggers for install-info (6.7.0.dfsg.2-5) ... Processing triggers for doc-base (0.10.9) ... Processing 1 added doc-base file...
5. Interpret a překladač – gsi a gsc
Gambit může být použit jak v režimu interpretru, tak i překladače. Interpret se spouští příkazem gsi. Z uživatelského pohledu se jedná o klasickou interaktivní smyčku REPL. Na rozdíl od Guile ve výchozím nastavení (pokud explicitně neinicializujete modul s readline) je možné pracovat s historií příkazů, používat kurzorové šipky, všechny základní editační příkazy známé z BASHe (záleží na .inputrc) atd.:
Obrázek 4: Interaktivní smyčka REPL spuštěná příkazem gsi.
Informace o konfiguraci interpretru v době jeho překladu zjistíme snadno:
$ gsi -v v4.8.8 20170202123920 x86_64-redhat-linux-gnu "./configure '--build=x86_64-redhat-linux-gnu' '--host=x86_64-redhat-linux-gnu' '--program-prefix=' '--disable-dependency-tracking' '--prefix=/usr' '--exec-prefix=/usr' '--bindir=/usr/bin' '--sbindir=/usr/sbin' '--sysconfdir=/etc' '--datadir=/usr/share' '--includedir=/usr/include' '--libdir=/usr/lib64' '--libexecdir=/usr/libexec' '--localstatedir=/var' '--sharedstatedir=/var/lib' '--mandir=/usr/share/man' '--infodir=/usr/share/info' '--enable-single-host' '--enable-c-opt' '--enable-gcc-opts' '--bindir=/usr/lib64/gambit-c/bin' '--libdir=/usr/lib64/gambit-c' 'build_alias=x86_64-redhat-linux-gnu' 'host_alias=x86_64-redhat-linux-gnu' 'CFLAGS=-O2 -g -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -m64 -mtune=generic' 'LDFLAGS=-Wl,-z,relro -specs=/usr/lib/rpm/redhat/redhat-hardened-ld' 'CXXFLAGS=-O2 -g -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -m64 -mtune=generic'"
Překladač Gambitu se spouští příkazem gsc:
$ gsc -exe factorial_3.scm $ ./factorial_3 3628800
$ time gsc -exe factorial_3.scm real 0m0.427s user 0m0.375s sys 0m0.048s
Lze se podívat i na přesnější informace o tom, jaké operace se při překladu provádí:
$ gsc -exe -verbose factorial_3.scm Parsing: print factorial "expr" Compiling: print factorial fact-iter Dumping: #<primitive | factorial_3|> #<procedure print> #<procedure factorial> Compilation finished. gcc -O2 -g -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -m64 -mtune=generic -Wno-unused -Wno-write-strings -O2 -fwrapv -fno-strict-aliasing -fno-trapping-math -fno-math-errno -fschedule-insns2 -fmodulo-sched -freschedule-modulo-scheduled-loops -fomit-frame-pointer -fPIC -fno-common -mieee-fp -mpc64 -D___SINGLE_HOST -I"/usr/include" -c -o "factorial_3.o" factorial_3.c gcc -O2 -g -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -m64 -mtune=generic -Wno-unused -Wno-write-strings -O2 -fwrapv -fno-strict-aliasing -fno-trapping-math -fno-math-errno -fschedule-insns2 -fmodulo-sched -freschedule-modulo-scheduled-loops -fomit-frame-pointer -fPIC -fno-common -mieee-fp -mpc64 -D___SINGLE_HOST -I"/usr/include" -c -o "factorial_3_.o" factorial_3_.c gcc -Wl,-z,relro -specs=/usr/lib/rpm/redhat/redhat-hardened-ld -O2 -g -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -m64 -mtune=generic -Wno-unused -Wno-write-strings -O2 -fwrapv -fno-strict-aliasing -fno-trapping-math -fno-math-errno -fschedule-insns2 -fmodulo-sched -freschedule-modulo-scheduled-loops -fomit-frame-pointer -fPIC -fno-common -mieee-fp -mpc64 -rdynamic -D___SINGLE_HOST -I"/usr/include" -o "factorial_3" factorial_3.o factorial_3_.o "/usr/lib64/gambit-c/libgambit.a" -lutil -ldl -lm
Opět můžeme zjistit, jaké přepínače a konfigurační volby byly použity při jeho vlastním překladu:
$ gsc -v v4.8.8 20170202123920 x86_64-redhat-linux-gnu "./configure '--build=x86_64-redhat-linux-gnu' '--host=x86_64-redhat-linux-gnu' '--program-prefix=' '--disable-dependency-tracking' '--prefix=/usr' '--exec-prefix=/usr' '--bindir=/usr/bin' '--sbindir=/usr/sbin' '--sysconfdir=/etc' '--datadir=/usr/share' '--includedir=/usr/include' '--libdir=/usr/lib64' '--libexecdir=/usr/libexec' '--localstatedir=/var' '--sharedstatedir=/var/lib' '--mandir=/usr/share/man' '--infodir=/usr/share/info' '--enable-single-host' '--enable-c-opt' '--enable-gcc-opts' '--bindir=/usr/lib64/gambit-c/bin' '--libdir=/usr/lib64/gambit-c' 'build_alias=x86_64-redhat-linux-gnu' 'host_alias=x86_64-redhat-linux-gnu' 'CFLAGS=-O2 -g -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -m64 -mtune=generic' 'LDFLAGS=-Wl,-z,relro -specs=/usr/lib/rpm/redhat/redhat-hardened-ld' 'CXXFLAGS=-O2 -g -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -m64 -mtune=generic'"
6. Použití interpretru dodávaného současně s Gambitem
Interpret spouštěný příkazem gsi se v mnoha ohledech podobá interpretru GNU Guile. Zadávané příkazy (resp. přesněji řečeno se jedná o formy a speciální příkazy) jsou vykonány ihned poté, co je forma ukončena a je stisknuta klávesa Enter. To znamená, že lze zadávat i víceřádkové formy, což jsme si ukázali v předchozí kapitole na příkladu faktoriálu. Při zápisu forem se navíc provádí „doskoky“ na právě uzavřené závorky, což je u LISPovského jazyka užitečné. Důležité jsou ovšem i speciální příkazy, kterými se ovládá vlastní REPL, popř. debugger, který je součástí REPLu. Tyto příkazy začínají čárkou, což znamená, že s objekty, jejichž název začíná taktéž čárkou (což Scheme umožňuje), se bude pracovat obtížně (ovšem kdo by používal takové identifikátory?).
Seznam všech dostupných příkazů REPLu získáme pomocí příkazy ,? (což je odlišné od GNU Guile!):
> ,? ,? : Summary of comma commands ,h | ,(h X) : Help on procedure of last error or procedure/macro named X ,q : Terminate the process ,qt : Terminate the current thread ,t : Jump to toplevel REPL ,d : Jump to enclosing REPL ,c | ,(c X) : Continue the computation with stepping off ,s | ,(s X) : Continue the computation with stepping on (step) ,l | ,(l X) : Continue the computation with stepping on (leap) ,N : Move to specific continuation frame (N>=0) ,N+ | ,N- : Move forward/backward by N continuation frames (N>=0) ,+ | ,- : Like ,1+ and ,1- ,++ | ,-- : Like ,N+ and ,N- with N = nb. of frames at head of backtrace ,y : Display one-line summary of current frame ,i : Display procedure attached to current frame ,b | ,(b X) : Display backtrace of current continuation or X (cont/thread) ,be | ,(be X) : Like ,b and ,(b X) but also display environment ,bed | ,(bed X) : Like ,be and ,(be X) but also display dynamic environment ,e | ,(e X) : Display environment of current frame or X (proc/cont/thread) ,ed | ,(ed X) : Like ,e and ,(e X) but also display dynamic environment ,st | ,(st X) : Display current thread group, or X (thread/thread group) ,(v X) : Start a REPL visiting X (proc/cont/thread)
7. Debugger, který je součástí REPLu
Nejdůležitější je podpora pro zjišťování problémů, které nastaly při běhu skriptů – jedná se tedy o základní debugging. Můžeme si to vyzkoušet na příkladu, který obsahuje chybu ve výpočtu:
(define (print item) (display item) (newline)) (define (factorial n) (if (= n 0) ; podmínka pro ukončení rekurzivního zanořování (/ n 0) ; chyba ve výpočtu (* n (factorial (- n 1))) ) )
Skript nejdříve načteme:
> (load "factorial_error.scm") "/home/ptisnovs/src/lisp-families/gambit/factorial_error.scm"
Zkusíme spustit výpočet, při němž dojde k chybě:
> (factorial 10) *** ERROR IN factorial, "factorial_error.scm"@8.14 -- Divide by zero (/ 0 0)
K chybě došlo až hluboko v rekurzivním výpočtu, takže se pokusíme získat další informace příkazem ,b:
1> ,b 0 factorial "factorial_error.scm"@8:14 (factorial (- n 1)) 1 factorial "factorial_error.scm"@8:14 (factorial (- n 1)) 2 factorial "factorial_error.scm"@8:14 (factorial (- n 1)) 3 factorial "factorial_error.scm"@8:14 (factorial (- n 1)) 4 factorial "factorial_error.scm"@8:14 (factorial (- n 1)) 5 factorial "factorial_error.scm"@8:14 (factorial (- n 1)) 6 factorial "factorial_error.scm"@8:14 (factorial (- n 1)) 7 factorial "factorial_error.scm"@8:14 (factorial (- n 1)) 8 factorial "factorial_error.scm"@8:14 (factorial (- n 1)) 9 factorial "factorial_error.scm"@8:14 (factorial (- n 1)) 10 (interaction) (console)@2:1 (factorial 10)
Podrobnější informace o tom, s jakými parametry byly funkce volány v rekurzivním výpočtu:
2> ,be 0 (interaction) (console)@4:33 x n = 1 1 factorial "factorial_error.scm"@8:14 (factorial (- n 1)) n = 1 2 factorial "factorial_error.scm"@8:14 (factorial (- n 1)) n = 2 3 factorial "factorial_error.scm"@8:14 (factorial (- n 1)) n = 3 4 factorial "factorial_error.scm"@8:14 (factorial (- n 1)) n = 4 5 factorial "factorial_error.scm"@8:14 (factorial (- n 1)) n = 5 6 factorial "factorial_error.scm"@8:14 (factorial (- n 1)) n = 6 7 factorial "factorial_error.scm"@8:14 (factorial (- n 1)) n = 7 8 factorial "factorial_error.scm"@8:14 (factorial (- n 1)) n = 8 9 factorial "factorial_error.scm"@8:14 (factorial (- n 1)) n = 9 10 factorial "factorial_error.scm"@8:14 (factorial (- n 1)) n = 10 11 (interaction) (console)@2:1 (factorial 10)
A konečně se můžeme v zásobníkových rámcích pohybovat a dostat se přesně na tu oblast, kterou je zapotřebí více prozkoumat:
2> ,bed 0 (interaction) (console)@4:33 x n = 1 (current-exception-handler) = primordial-exception-handler (current-input-port) = '#<input-output-port #2 (console)> (current-output-port) = '#<input-output-port #2 (console)> (current-directory) = "/home/ptisnovs/src/other/lisp-families/gambit/" 1 factorial "factorial_error.scm"@8:14 (factorial (- n 1)) n = 1 (current-exception-handler) = primordial-exception-handler (current-input-port) = '#<input-output-port #2 (console)> (current-output-port) = '#<input-output-port #2 (console)> (current-directory) = "/home/ptisnovs/src/other/lisp-families/gambit/" 2 factorial "factorial_error.scm"@8:14 (factorial (- n 1)) n = 2 (current-exception-handler) = primordial-exception-handler (current-input-port) = '#<input-output-port #2 (console)> (current-output-port) = '#<input-output-port #2 (console)> (current-directory) = "/home/ptisnovs/src/other/lisp-families/gambit/" 2> ,2+ 2 factorial "factorial_error.scm"@8.14 (factorial (- n 1))
8. Základní vlastnosti jazyka Scheme
Gambit podporuje všechny vlastnosti popsané v R4RS i R5RS, takže jen v krátkosti (většinu příkladů jsme již viděli s výjimkou continuations a práci s výjimkami).
Práce s tečka-dvojicemi:
(print '(1 . 2)) (print '(1 . ((2 . 3) . 4))) (print '((1 . 2) . (3 . 4))) ; this is proper list (print '(1 . (2 . (3 . nil)))) ; this is proper list (print '(1 . (2 . (3 . ()))))
Konstrukce seznamů:
(print (cons 1 2)) (print (cons 1 (cons 2 3))) (print '((1 . 2) . (3 . 4))) ; this is proper list (print (cons 1 (cons 2 (cons 3 '())))) (define nil '()) ; this is proper list (print (cons 1 (cons 2 (cons 3 nil))))
Přístup k prvkům seznamů:
(print '(1 2 3 4)) (print (list 1 2 3 4)) ; create list and assign it to symbol ; (=variable) (define a '(1 2 3 4)) ; get the first item (print (car a)) ; get the rest of a list (print (cdr a)) ; combination of car+cdr (print (cadr a)) ; combination of cdr+cdr (print (cddr a))
Standardní predikáty:
(define nil '()) (print "pair?") (print (pair? nil)) (print (pair? #t)) (print (pair? 42)) (print (pair? "string")) (print (pair? '(1 2 3))) (newline) (print "list?") (print (list? nil)) (print (list? #t)) (print (list? 42)) (print (list? "string")) (print (list? '(1 2 3))) (newline) (print "null?") (print (null? nil)) (print (null? #t)) (print (null? 42)) (print (null? "string")) (print (null? '(1 2 3))) (newline) (print "zero?") (print (zero? 0)) (print (zero? 42)) (newline)
Blok begin s formami, které se postupně vykonávají:
(begin (print "pair?") (print (pair? nil)) (print (pair? #t)) (print (pair? 42)) (print (pair? "string")) (print (pair? '(1 2 3))) (newline)) (begin (print "list?") (print (list? nil)) (print (list? #t)) (print (list? 42)) (print (list? "string")) (print (list? '(1 2 3))) (newline)) (begin (print "null?") (print (null? nil)) (print (null? #t)) (print (null? 42)) (print (null? "string")) (print (null? '(1 2 3))) (newline)) (begin (print "zero?") (print (zero? 0)) (print (zero? 42)) (newline))
Logické operace a použití speciálních forem and a or namísto if:
(define nil '()) (print (and #t #t)) (print (and #t nil)) (print (or #t #t)) (print (or #t nil)) (print (or nil nil #t nil)) (print (or nil nil nil nil)) (print (not nil)) (print (not #t)) (define x 10) (print (if (> x 0) "kladne" "zaporne")) (define y 0) (print (if (> y 0) "kladne" "zaporne")) (define x 10) (print (and (> x 0) "kladne")) (define y 0) (print (and (> y 0) "kladne"))
Speciální forma cond zobecňující rozeskoky:
(define (sgn n) (cond ((< n 0) 'negative) ((> n 0) 'positive) ((zero? n) 'zero))) (print "sgn") (print (sgn -10)) (print (sgn 0)) (print (sgn 10)) (newline) (define (sgn-2 n) (cond ((< n 0) 'negative) ((> n 0) 'positive) (#t 'zero))) (print "sgn-2") (print (sgn-2 -10)) (print (sgn-2 0)) (print (sgn-2 10)) (newline) (define (sgn n) (cond ((not (number? n)) 'not-a-number) ((< n 0) 'negative) ((> n 0) 'positive) ((zero? n) 'zero))) (print (sgn -10)) (print (sgn 0)) (print (sgn 10)) (print (sgn '()))
Konstrukce vektorů, přístup k prvkům vektorů:
(define vector1 '#(1 2 3 4)) (display vector1) (newline) (display (vector-ref vector1 0)) (display (vector-ref vector1 10))
Mutace prvků vektorů:
(define vector2 (vector 1 2 3 4 5)) (display vector2) (newline) (display (vector-ref vector2 0)) (newline) (vector-set! vector2 2 -1) (display vector2) (newline)
Anonymní funkce:
(lambda () (print "no parameters")) (lambda (a) (print "one parameter")) (lambda (a b) (print "two parameters")) (lambda (a . b) (print "at least one parameter")) (lambda (a b . c) (print "at least two parameters")) (lambda x (print "zero or more parameters"))
Pojmenované funkce:
; one-liner function (define (add x y) (+ x y)) ; function written on more lines (define (mul x y) (* x y)) ; function written on more lines using lambda (define div (lambda (x y) (* x y))) ; test functions (print (add 1 2)) (print (mul 6 7)) (print (div 10 3))
Uzávěry:
(define counter (let ((i -1)) (lambda () (set! i (+ i 1)) i))) (print (counter)) (print (counter)) (print (counter))
Více nezávislých čítačů:
(define (get-counter) (let ((i -1)) (lambda () (set! i (+ i 1)) i))) (define counter1 (get-counter)) (define counter2 (get-counter)) (print (counter1)) (print (counter1)) (print (counter1)) (print (counter2)) (print (counter2)) (print (counter2)) (print (counter1)) (print (counter1)) (print (counter1))
Použití quote a quasiquote (nejenom) při konstrukci seznamů:
(define b (list 1 2 3)) (print "quote:") (print '(a b c)) (print '(a ,b c)) (print '(a ,@b c)) (newline) (print "quasiquote:") (print `(a b c)) (print `(a ,b c)) (print `(a ,@b c)) (newline)
Novinkou (resp. ne zcela běžnou věcí) je zachytávání výjimek, které je ukázáno na příkladu s funkcí, která nevyhodí výjimku při dělení nulou. Namísto toho vrátí #f, což je výsledek anonymní funkce akceptující objekt s chybou:
(define (safe-div x y) (with-exception-catcher (lambda (error) #f) (lambda () (/ x y)))) (print (safe-div 10 3)) (print (safe-div 10 0))
A poslední ne vždy implementovanou vlastností jsou takzvané continuations (pokračování), což je obecně velmi užitečná programovací technika, kterou si vysvětlíme v samostatném článku:
(define c1 #f) (define (test-cc) (let ((i 0)) (call/cc (lambda (k) (set! c1 k))) (set! i (+ i 1)) i))
9. Rozšíření zavedená (nejenom) systémem Gambit – nepovinné parametry funkcí
Gambit, podobně jako některé další varianty jazyka Scheme, umožňuje volání funkcí s proměnným počtem parametrů, popř. s nepovinnými parametry. Funkce s nepovinnými parametry se používají následovně:
(define (f1) (print "no parameters")) (define (f2 a) (print "one parameter") (print a)) (define (f3 a b) (print "two parameters") (print a) (print b)) (define (f4 a #!optional b) (print "at least one parameter") (print a) (print b)) (define (f5 a b #!optional c) (print "at least two parameters") (print a) (print b) (print c)) (define (f6 a #!optional (b -1)) (print "at least one parameter") (print a) (print b)) (define (f7 a b #!optional (c -1)) (print "at least two parameters") (print a) (print b) (print c)) (f1) (f2 10) (f3 1 2) (f4 1) (f4 1 2) (f5 1 2) (f5 1 2 3) (f6 1) (f6 1 2) (f7 1 2) (f7 1 2 3)
S výsledky:
no parameters one parameter 10 two parameters 1 2 at least one parameter 1 #f at least one parameter 1 2 at least two parameters 1 2 #f at least two parameters 1 2 3 at least one parameter 1 -1 at least one parameter 1 2 at least two parameters 1 2 -1 at least two parameters 1 2 3
10. Další možnosti volání funkcí
Použití proměnného počtu parametrů – zbylé parametry se uloží do seznamu za tečkou:
(define (f1) (print "no parameters")) (define (f2 a) (print "one parameter") (print a)) (define (f3 a b) (print "two parameters") (print a) (print b)) (define (f4 a . b) (print "at least one parameter") (print a) (print b)) (define (f5 a b . c) (print "at least two parameters") (print a) (print b) (print c)) (f1) (f2 10) (f3 1 2) (f4 1) (f4 1 2) (f4 1 2 3) (f5 1 2) (f5 1 2 3) (f5 1 2 3 4)
Výsledky:
no parameters one parameter 10 two parameters 1 2 at least one parameter 1 () at least one parameter 1 (2) at least one parameter 1 (2 3) at least two parameters 1 2 () at least two parameters 1 2 (3) at least two parameters 1 2 (3 4)
Pojmenované parametry:
(define (f b #!key c (d 7) (e 8)) (print b) (print c) (print e) (print e) (newline)) (f 0) (f 1 c: 2) (f 1 e: 5) (f 1 c: 2 d: 3 e: 4)
Výsledky:
0 #f 8 8 1 2 8 8 1 #f 5 5 1 2 4 4
A konečně kombinace nepovinných parametrů a tzv.„rest“ (zbývajících) parametrů:
(define (f1 #!optional (a 4) #!key (b 5) c) (print a) (print b) (print c) (newline)) (define (f2 #!key (i 5) #!rest j) (print i) (print j) (newline)) (f1) (f1 1) (f1 2 b: 3) (f1 4 b: 5 c: 6) (f1 7 c: 8) (f2 9) (f2 10 i: 11) (f2 i: 12 13)
11. Definice nových datových typů (struktur)
S využitím formy define-type je možné definovat tvar nové datové struktury, pro kterou se navíc automaticky vytvoří konstruktor, funkce pro přístup k prvkům struktury (gettery), funkce pro modifikaci prvků struktury (settery, mutátory) i predikáty pro nový datový typ. Podívejme se nyní na jednoduchý příklad s datovou strukturou pojmenovanou user s prvky nazvanými name a surname. Po zavolání define-type se vytvoří konstruktor make-user, gettery user-name a user-surname i mutátory user-name-set! a user-surname-set!. Zapomenout nesmíme ani na predikát user?:
(define (print item) (display item) (newline)) (define-type user name surname) ; konstruktor (define john (make-user "John" "?")) (print john) ; gettery (print (user-name john)) (print (user-surname john)) ; mutatory (settery) (user-name-set! john "Real John") (user-surname-set! john "Doe") (print john) (print (user-name john)) (print (user-surname john)) ; predikaty (print (user? 42)) (print (user? john))
Příklad použití:
#<user #2 name: "John" surname: "?"> John ? #<user #2 name: "Real John" surname: "Doe"> Real John Doe #f #t
12. Příprava jednoduchého benchmarku
V úvodních kapitolách jsme si řekli, že projekt Gambit může být pro vývojáře zajímavý mj. i proto, že je vybaven rychlým a plnohodnotným překladačem. Zkusme tedy zjistit, jak kvalitní kód tento překladač dokáže generovat; provedeme přitom porovnání s již dříve popsanými implementacemi programovacího jazyka Scheme, tedy zejména s GNU Guile a Chicken Scheme. Pro tento účel si připravíme jednoduchý benchmark, s nímž jsme se vlastně již seznámili v předchozích článcích. Jedná se o program určený pro iterativní výpočet konstanty π s využitím (velmi pomalu konvergujícího) Wallisova součinu (Wallis product, který je popsán například na stránce https://en.wikipedia.org/wiki/Wallis_product). Jeden z možných přepisů tohoto algoritmu do Scheme může vypadat následovně:
(define (compute-pi n) (let ((pi 4.0)) (do ((i 3 (+ i 2))) ((> i (+ n 2))) (set! pi (* pi (/ (- i 1) i) (/ (+ i 1) i)))) pi)) (do ((n 1 (* n 2))) ((> n 10000000)) (display n) (display " ") (display (compute-pi n)) (newline))
(define (compute-pi n) (let loop ((pi 4.0) (i 3)) (if (<= i (+ n 2)) (loop (* pi (/ (- i 1) i) (/ (+ i 1) i)) (+ i 2)) pi))) (let loop ((n 1)) (display n) (display " ") (display (compute-pi n)) (newline) (if (<= n 10000000) (loop (* n 2))))
Výše uvedený benchmark použijeme v následujících kapitolách pro otestování kvality či nekvality výsledného kódu generovaného Gambitem-C.
Pro porovnání přepíšeme benchmark i do jazyka Clojure:
(ns pi-1.core (:gen-class)) (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))) (defn -main [& args] (loop [n 1] (print n "\t") (println (compute-pi n)) (if (< n 5000000) (recur (* n 2)))))
13. Porovnání rychlosti interpretrů Gambit, GNU Guile a Chicken Scheme
S projektem nazvaným GNU Guile jsme se již v tomto seriálu seznámili. Jedná se o interpret jazyka Scheme doplněný o JIT překladač, takže můžeme předpokládat, že by výpočetní výkon takto doplněného interpretru mohl být vyšší, než je tomu v případě jednoduchého interpretru dodávaného s projektem Chicken Scheme. Ostatně se o tomto předpokladu můžeme snadno přesvědčit, protože výše popsaný výpočet konstanty π je napsán takovým způsobem, že je kompatibilní jak s GNU Guile, tak i s Gambitem a Chicken Scheme (ovšem pozor – ne všechny konstrukce je možné mezi těmito implementacemi Scheme bez problémů přenášet i přesto, že je zachována kompatibilita s RnRS).
Nejprve tedy výpočet spustíme v GNU Guile a budeme sledovat jak strojový čas strávený výpočtem, tak i celkový čas viditelný vnějším pozorovatelem (což je mnohdy důležitější ukazatel):
$ time guile pi1.scm
Výsledky, na jejichž konci jsou zobrazeny i časové údaje:
1 3.5555555555555554 2 3.5555555555555554 4 3.4133333333333336 8 3.302393550012597 16 3.230036466411716 32 3.1881271694471383 64 3.1654820600347926 128 3.1536988490957967 256 3.147686899556418 512 3.1446501625172 1024 3.143124017028185 2048 3.142358989121772 4096 3.141975985005608 8192 3.1417843602347433 16384 3.1416885171495856 32768 3.1416405879293077 65536 3.1416166213993866 131072 3.1416046376544267 262144 3.1415986456618494 524288 3.141595649635512 1048576 3.141594151614876 2097152 3.141593402602468 4194304 3.1415930280955355 8388608 3.1415928408418403 real 0m4.858s user 0m5.212s sys 0m0.058s
Nyní si vyzkoušejme, jestli bude výpočet π podle stejného skriptu s využitím interpretru nástroje Chicken Scheme rychlejší nebo pomalejší. Použijeme přepínače -b a -q, které zajistí, že se interpret po dokončení výpočtů ihned ukončí a že se na začátku nebudou zobrazovat úvodní informace o projektu Chicken Scheme (což sice nevede k žádnému podstatnému urychlení, ale ke zpřesnění výsledných časů):
$ time csi -b -q pi1.scm
Samotné výsledky výpočtu by měly být přibližně podobné, minimálně na prvních sedmi až osmi desetinných místech:
1 3.55555555555556 2 3.55555555555556 4 3.41333333333333 8 3.3023935500126 16 3.23003646641172 32 3.18812716944714 64 3.16548206003479 128 3.1536988490958 256 3.14768689955642 512 3.1446501625172 1024 3.14312401702818 2048 3.14235898912177 4096 3.14197598500561 8192 3.14178436023474 16384 3.14168851714959 32768 3.14164058792931 65536 3.14161662139939 131072 3.14160463765443 262144 3.14159864566185 524288 3.14159564963551 1048576 3.14159415161488 2097152 3.14159340260247 4194304 3.14159302809554 8388608 3.14159284084184 real 0m8.137s user 0m7.739s sys 0m0.318s
Čas celého výpočtu je v tomto případě výrazně delší, než tomu je v porovnání s projektem GNU Guile. Dále můžeme při porovnání všech tří časů předpokládat, že výpočet běžel pouze v jediném vláknu (což je ostatně pravda).
Nakonec vyzkoušíme stejný výpočet (a naprosto stejný zdrojový kód) spustit v intepretru Gambitu:
$ time gsi pi_1.scm
Výsledky:
1 3.5555555555555554 2 3.5555555555555554 4 3.4133333333333336 8 3.302393550012597 16 3.230036466411716 32 3.1881271694471383 64 3.1654820600347926 128 3.1536988490957967 256 3.147686899556418 512 3.1446501625172 1024 3.143124017028185 2048 3.142358989121772 4096 3.141975985005608 8192 3.1417843602347433 16384 3.1416885171495856 32768 3.1416405879293077 65536 3.1416166213993866 131072 3.1416046376544267 262144 3.1415986456618494 524288 3.141595649635512 1048576 3.141594151614876 2097152 3.141593402602468 4194304 3.1415930280955355 8388608 3.1415928408418403 real 0m46.689s user 0m46.610s sys 0m0.007s
Stručné porovnání výsledků:
# | Interpret | real | user | sys |
---|---|---|---|---|
1 | GNU Guile | 4,858 | 5,212 | 0,058 |
2 | Chicken Scheme | 8,137 | 7,738 | 0,318 |
3 | Gambit (gsi) | 46,689 | 46,610 | 0,007 |
14. Rychlost benchmarku přeloženého do nativního kódu
Nyní se pokusme náš testovací příklad přeložit a zjistit, jak a zda došlo k urychlení výpočtů:
$ gsc -exe pi_1.scm
Pokud se překlad povedl, spustíme benchmark:
$ time ./pi_1
Výsledky:
real 0m37.454s user 0m37.390s sys 0m0.008s
15. Přepis algoritmu takovým způsobem, aby používal výpočty s hodnotami s plovoucí řádovou čárkou
Algoritmus je možné v případě potřeby ještě vyššího výpočetního výkonu přepsat takovým způsobem, aby se výpočty prováděly s hodnotami s plovoucí řádovou čárkou. To se zajistí jak deklarací mostly-flonum užitečné pro překladač, tak i použitím operátorů fl*, fl/ a fl+ namísto operátorů *, / a +:
(declare (mostly-flonum) (block) (not safe)) (define (compute-pi n) (let ((pi 4.0)) (do ((i 3.0 (fl+ i 2.0))) ((fl> i (fl+ n 2.0))) (set! pi (fl* pi (fl/ (fl- i 1.0) i) (fl/ (fl+ i 1.0) i)))) pi)) (do ((n 1.0 (* n 2))) ((> n 10000000)) (display n) (display " ") (display (compute-pi n)) (newline))
Výsledek běhu interpretru (gsi):
1. 3.5555555555555554 2. 3.5555555555555554 4. 3.4133333333333336 8. 3.302393550012597 16. 3.230036466411716 32. 3.1881271694471383 64. 3.1654820600347926 128. 3.1536988490957967 256. 3.147686899556418 512. 3.1446501625172 1024. 3.143124017028185 2048. 3.142358989121772 4096. 3.141975985005608 8192. 3.1417843602347433 16384. 3.1416885171495856 32768. 3.1416405879293077 65536. 3.1416166213993866 131072. 3.1416046376544267 262144. 3.1415986456618494 524288. 3.141595649635512 1048576. 3.141594151614876 2097152. 3.141593402602468 4194304. 3.1415930280955355 8388608. 3.1415928408418403
Čas dosažený při interpretaci:
real 0m8.188s user 0m8.169s sys 0m0.008s
Ještě si vyzkoušíme rychlost výpočtu po překladu, kde by se měly plně projevit optimalizace při překladu:
$ gsc -exe pi_3_flonum.scm
Výsledky výpočtů:
$ time ./pi_3_flonum 1. 3.5555555555555554 2. 3.5555555555555554 4. 3.4133333333333336 8. 3.302393550012597 16. 3.230036466411716 32. 3.1881271694471383 64. 3.1654820600347926 128. 3.1536988490957967 256. 3.147686899556418 512. 3.1446501625172 1024. 3.143124017028185 2048. 3.142358989121772 4096. 3.141975985005608 8192. 3.1417843602347433 16384. 3.1416885171495856 32768. 3.1416405879293077 65536. 3.1416166213993866 131072. 3.1416046376544267 262144. 3.1415986456618494 524288. 3.141595649635512 1048576. 3.141594151614876 2097152. 3.141593402602468 4194304. 3.1415930280955355 8388608. 3.1415928408418403
Dosažený čas:
real 0m0.153s user 0m0.141s sys 0m0.011s
16. Použití vláken, paralelizace výpočtů
Jednou z předností Gambitu je relativně jednoduchá paralelizace výpočtů (i když ne tak jednoduchá, jako by tomu bylo v případě použití gorutin a kanálů). V runtime lze výpočty upravit takovým způsobem, aby počet vláken odpovídal počtu procesorových jader. Existuje přitom funkce nazvaná ##cpu-count vracející počet reálně dostupných jader a taktéž funkce current-vm-processor-count vracející počet vláken OS, které lze využít (s tím, že mapování vláken na CPU je ponecháno na operačním systému).
Vytvoření vlákna zajistí forma make-thread, které se předá kód, který se má spustit. Spuštění vlákna se provede s využitím thread-start!, čekání na dokončení vlákna pak pomocí thread-join!. Podívejme se nyní, jak lze předchozí kód paralelizovat i s akumulací výsledků:
; původní funkce pro výpočet se nemusí měnit (define (compute-pi n) (let ((pi 4.0)) (do ((i 3 (+ i 2))) ((> i (+ n 2))) (set! pi (* pi (/ (- i 1) i) (/ (+ i 1) i)))) pi)) ; pomocná funkce pro výpočet a zobrazení výsledků (define (pi-for n) (let ((pi (compute-pi n))) (display n) (display "\t") (display pi) (newline))) ; vytvoření seznamu se vstupními hodnotami n (define (power-of-two n maximum) (if (< n maximum) (cons n (power-of-two (* n 2) maximum)) (list n))) ; vytvoření výpočetních vláken (define threads (map (lambda (n) (make-thread (lambda () (pi-for n)))) (power-of-two 1 5000000))) ; spuštění všech vláken (for-each thread-start! threads) ; čekání na dokončení výpočtů ve všech vláknech (for-each thread-join! threads)
Čas výpočtů:
$ time ./pi_parallel 1 3.5555555555555554 2 3.5555555555555554 4 3.4133333333333336 8 3.302393550012597 16 3.230036466411716 32 3.1881271694471383 64 3.1654820600347926 128 3.1536988490957967 256 3.147686899556418 512 3.1446501625172 1024 3.143124017028185 2048 3.142358989121772 4096 3.141975985005608 8192 3.1417843602347433 16384 3.1416885171495856 32768 3.1416405879293077 65536 3.1416166213993866 131072 3.1416046376544267 262144 3.1415986456618494 524288 3.141595649635512 1048576 3.141594151614876 2097152 3.141593402602468 4194304 3.1415930280955355 8388608 3.1415928408418403 real 0m36.867s user 0m36.800s sys 0m0.013s
Paralelní verze s flonum:
(declare (mostly-flonum) (block) (not safe)) (define (compute-pi n) (let ((pi 4.0)) (do ((i 3.0 (fl+ i 2.0))) ((fl> i (fl+ n 2.0))) (set! pi (fl* pi (fl/ (fl- i 1.0) i) (fl/ (fl+ i 1.0) i)))) pi)) (define (pi-for n) (let ((pi (compute-pi n))) (display n) (display "\t") (display pi) (newline))) (define (power-of-two n maximum) (if (< n maximum) (cons n (power-of-two (* n 2) maximum)) (list n))) (define threads (map (lambda (n) (make-thread (lambda () (pi-for n)))) (power-of-two 1.0 5000000))) (for-each thread-start! threads) (for-each thread-join! threads)
Výsledek výpočtu po překladu:
$ time ./pi_parallel_flonum 1. 3.5555555555555554 2. 3.5555555555555554 4. 3.4133333333333336 8. 3.302393550012597 16. 3.230036466411716 32. 3.1881271694471383 64. 3.1654820600347926 128. 3.1536988490957967 256. 3.147686899556418 512. 3.1446501625172 1024. 3.143124017028185 2048. 3.142358989121772 4096. 3.141975985005608 8192. 3.1417843602347433 16384. 3.1416885171495856 32768. 3.1416405879293077 65536. 3.1416166213993866 131072. 3.1416046376544267 262144. 3.1415986456618494 524288. 3.141595649635512 2097152. 3.141593402602468 1048576. 3.141594151614876 4194304. 3.1415930280955355 8388608. 3.1415928408418403 real 0m0.159s user 0m0.150s sys 0m0.009s
V Clojure bude paralelní výpočet vypadat takto:
(ns pi-2.core (:gen-class)) (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))) (def power-of-2 (iterate (partial *' 2) 2)) (defn -main [& args] (let [ns (take-while (partial > 10000000) power-of-2) pi (pmap compute-pi ns) result (map vector ns pi)] (doseq [r result] (println (first r) "\t" (nth r 1)))) (shutdown-agents))
17. Porovnání všech benchmarků
V této kapitole budou všechny benchmarky porovnány. V případě jazyka Clojure byl benchmark spuštěn přímo příkazem lein run (tedy vlastně v režimu interpretace – resp. přesněji řečeno průběžného překladu do bajtkódu) a následně byl proveden překlad do bajtkódu s uložením do JARu se spuštěním bajtkódu. Obě alternativy vypadají následovně:
$ time lein run
$ lein uberjar $ time java -jar pi_1-0.1.0-SNAPSHOT-standalone.jar
Následuje tabulka s výsledky benchmarků:
# | Interpret/runtime | Varianta | real | user | sys |
---|---|---|---|---|---|
1 | GNU Guile | interpret | 4,858 | 5,212 | 0,058 |
2 | Chicken Scheme | interpret | 8,137 | 7,738 | 0,318 |
3 | Gambit (gsi) | interpret | 46,689 | 46,610 | 0,007 |
4 | Gambit (gsi) | interpret (flonum) | 8,188 | 8,169 | 0,008 |
5 | Gambit (gsc) | přeložený | 37,454 | 37,390 | 0,008 |
6 | Gambit (gsc) | přeložený (flonum) | 0,153 | 0,141 | 0,011 |
7 | Gambit (gsc) | přeložený (vlákna) | 36,867 | 36,800 | 0,013 |
8 | Gambit (gsc) | přeložený (vlákna+flonum) | 0,159 | 0,150 | 0,009 |
9 | Clojure | interpret | 14,925 | 16,365 | 0,515 |
10 | Clojure | bajtkód | 6,107 | 7,177 | 0,394 |
11 | Clojure | interpret, vlákna | 10,684 | 18,007 | 0,634 |
12 | Clojure | bajtkód, vlákna | 3,691 | 8,765 | 0,491 |
Obrázek 5: Výsledky benchmarků.
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
- Brian Harvey
„Simply Scheme: Introducing Computer Science“
1999 MIT
Dostupné online na adrese https://people.eecs.berkeley.edu/~bh/ss-toc2.html - Paul R. Wilson
„An Introduction to Scheme and its Implementation“
1997
Dostupné online na adrese https://www.cs.utexas.edu/ftp/garbage/cs345/schintro-v14/schintro_toc.html - Dorai Sitaram
„Teach Yourself Scheme in Fixnum Days“
1998–2015
Dostupné online na adrese https://ds26gte.github.io/tyscheme/ - 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, Barbara Kaiser, Karl Knight
„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 adrese 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
20. Předchozí části seriálu
V této kapitole jsou uvedeny odkazy na všechny předchozí části seriálu o světě programovacích jazyků LISP a Scheme:
- Jemný úvod do rozsáhlého světa jazyků LISP a Scheme
https://www.root.cz/clanky/jemny-uvod-do-rozsahleho-sveta-jazyku-lisp-a-scheme/ - PicoLisp: minimalistický a přitom překvapivě výkonný interpret Lispu
https://www.root.cz/clanky/picolisp-minimalisticky-a-pritom-prekvapive-vykonny-interpret-lispu/ - PicoLisp: užitečné funkce a speciální formy používané při tvorbě aplikací
https://www.root.cz/clanky/picolisp-uzitecne-funkce-a-specialni-formy-pouzivane-pri-tvorbe-aplikaci/ - PicoLisp: dokončení popisu a několik praktických rad na závěr
https://www.root.cz/clanky/picolisp-dokonceni-popisu-a-nekolik-praktickych-rad-na-zaver/ - GNU Guile – interpret Scheme vestavitelný do nativních aplikací
https://www.root.cz/clanky/gnu-guile-interpret-scheme-vestavitelny-do-nativnich-aplikaci/ - TinyScheme aneb další interpret jazyka Scheme vestavitelný do dalších aplikací
https://www.root.cz/clanky/tinyscheme-aneb-dalsi-interpret-jazyka-scheme-vestavitelny-do-dalsich-aplikaci/ - Kawa: překvapivě silný a výkonný dialekt Scheme pro JVM
https://www.root.cz/clanky/kawa-prekvapive-silny-a-vykonny-dialekt-scheme-pro-jvm/ - Jazyk Kawa v ekosystému virtuálního stroje Javy
https://www.root.cz/clanky/jazyk-kawa-v-ekosystemu-virtualniho-stroje-javy/ - Zpracování vektorů, matic a N-rozměrných polí v programovacím jazyku Kawa
https://www.root.cz/clanky/zpracovani-vektoru-matic-a-n-rozmernych-poli-v-programovacim-jazyku-kawa/ - 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/ - Jazyk Joker: dialekt Clojure naprogramovaný v Go
https://www.root.cz/clanky/jazyk-joker-dialekt-clojure-naprogramovany-v-go/ - Chicken Scheme – další interpret a především překladač programovacího jazyka Scheme
https://www.root.cz/clanky/chicken-scheme-dalsi-interpret-a-predevsim-prekladac-programovaciho-jazyka-scheme/
21. Odkazy na Internetu
- Gambit in the browser
https://feeley.github.io/gambit-in-the-browser/ - A Tour of Scheme in Gambit
http://dynamo.iro.umontreal.ca/wiki/images/a/a7/A_Tour_of_Scheme_in_Gambit.pdf - Gambit Scheme: Inside Out
http://www.iro.umontreal.ca/~gambit/Gambit-inside-out.pdf - Gambit Internal Documentation
http://dynamo.iro.umontreal.ca/wiki/index.php/Internal_Documentation - clojure-scheme: Compiling to Native Code via Scheme
http://www.iro.umontreal.ca/~gambit/Sorenson-Clojure-to-Native-via-Scheme.pdf - Gauche – a Scheme implementation
http://practical-scheme.net/gauche/ - Scheme48
https://s48.org/ - SISC (Second Interpreter of Scheme)
http://sisc-scheme.org/ - The SCM Implementation of Scheme
https://people.csail.mit.edu/jaffer/SCM.html - Ypsilon – The ultimate script language system for the video pinball fourth generation
http://www.littlewingpinball.com/doc/en/ypsilon/index.html - Chicken Scheme
https://call-cc.org/ - Eggs Unlimited
http://wiki.call-cc.org/chicken-projects/egg-index-5.html - Chicken Scheme Wiki
https://wiki.call-cc.org/ - CHICKEN for Python programmers
https://wiki.call-cc.org/chicken-for-python-programmers - Programming for Performance
http://wiki.call-cc.org/programming-for-performance - Using the compiler
https://wiki.call-cc.org/man/4/Using%20the%20compiler - CHICKEN Scheme tutorials
https://wiki.call-cc.org/tutorials - 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/ - 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) - Vector Library (R7RS-compatible)
https://srfi.schemers.org/srfi-133/srfi-133.html - 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 - Vectors (pro Gauche)
https://practical-scheme.net/gauche/man/gauche-refe/Vectors.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í - 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/ - 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 - Economy Size Geek – Interview with Rich Hickey, Creator of Clojure
https://www.linuxjournal.com/article/10708