Common Lisp: žralok mezi programovacími jazyky

14. 4. 2022
Doba čtení: 56 minut

Sdílet

 Autor: Conrad Barski, podle licence: Public domain
V seriálu o dialektech programovacího jazyka Lisp nesmí chybět článek o Common Lispu. Jedná se o multiparadigmatický programovací jazyk definovaný standardem (ANSI), pro nějž existuje několik vzájemně kompatibilních implementací.

Obsah

1. Common Lisp – žralok mezi programovacími jazyky

2. Stručná historie vývoje Common Lispu

3. Interaktivní vývoj – skutečný REPL a koncept „obrazu“ prostředí

4. Instalace Common Lispu a nastavení prostředí

5. Interní debugger Common Lispu

6. První krůčky s Common Lispem

7. Přerušení výpočtu a jeho obnovení

8. Multiparadigmatický programovací jazyk

9. Makrosystém Common Lispu

10. LISP jako jazyk pro tvorbu (doménově specifických) jazyků

11. DSL pro tvorbu programových smyček – makro loop

12. Použití klauzulí while a until

13. Různé varianty počítané smyčky typu for

14. Procházení prvky seznamu aneb smyčka typu for-each

15. Vybrané další možnosti poskytované makrem loop

16. Překlad LISPovského programu do nativního kódu

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

18. Předchozí části seriálu

19. Literatura

20. Odkazy na Internetu

1. Common Lisp – žralok mezi programovacími jazyky

„Most languages are used to solve problem. Lisp is one of the few languages that allows you to write programs that write programs that solve problems“

Svět LISPovských jazyků je dnes velmi rozsáhlý a poměrně nepřehledný, ovšem není se čemu divit, protože první koncept LISPu vznikl již před neuvěřitelnými 61 lety, konkrétně v roce 1958. Jedná se tedy o jeden z prvních vyšších programovacích jazyků vyvinutých pro nasazení na mainframech. Některé z těchto jazyků jsou (s modifikacemi a vylepšeními) používány dodnes, a to nikoli pouze z důvodu konzervativnosti programátorů či nutnosti údržby starého programového kódu stále používaného v produkčním prostředí. V LISPu se totiž objevilo hned několik zajímavých a přelomových konceptů, které samy o sobě dokázaly udržet tento programovací jazyk v hledáčku programátorů. A nejenom to – mnoho myšlenek z LISPu se postupně uplatňuje i v dalších programovacích jazycích, i když je nutné říci, že některé koncepty (homoikonicita a s ní související makra a metaprogramování) jsou do mnoha dalších jazyků přenositelná jen s obtížemi (poměrně úspěšné jsou v tomto ohledu jazyky Rust a Julia se svými systémy maker).

Obrázek 1: O Common Lispu bylo vydáno i relativně velké množství knih.

Dialektů LISPu vzniklo velké množství – určitě několik desítek, pravděpodobně i několik set. Ovšem v současnosti se setkáme především s Common Lispem, Elispem (v Emacsu), jazykem Racket a s Clojure. Dnes se budeme zabývat prvně jmenovaným, tj. Common Lispem. Ten je od roku 1994 definován v ANSI normě a existuje několik jeho implementací, které jsou mezi sebou kompatibilní (zcela jistě na úrovni zdrojových kódů a základních knihoven popsaných v ANSI):

  • Steel Bank Common Lisp (SBCL)
  • Embeddable Common Lisp (ECL), je překládán do C
  • CLISP
  • ABCL, zajišťuje interoperabilitu s JVM
  • ClozureCL
  • CLASP, založena na iteroperabilitě s LLVM
  • AllegroCL (proprietary)
  • LispWorks (proprietary)
  • CMUCL, původně vznikl na Carnegie Mellon University
  • GNU Common Lisp
Poznámka: název Steel Bank Common Lisp vznikl jako určitá slovní hříčka. Jedná se o referenci na CMUCL pocházející z Carnegie Mellon University. Andrew Carnegie přitom podnikal v oblasti ocelářství (steel), Andrew Mellon v oblasti bankovnictví (bank).

Okolo Common Lispu vznikl poměrně rozsáhlý ekosystém, viz například stránku https://github.com/CodyRe­ichert/awesome-cl.

Obrázek 2: Přebal další knihy o Common Lispu.

2. Stručná historie vývoje Common Lispu

„Common Lisp isn't going anywhere,
and that's a great thing“
Alan Dipert

V této kapitole si alespoň ve stručnosti přiblížíme historii Common Lispu. Ten původně vznikl z Maclispu, což je jeden z dialektů programovacího jazyka LISP, kterému jsme se (i když taktéž jen ve stručnosti) věnovali v článku Interlisp aneb oživujeme dinosaura. Mezi cíle, které si tým, který vznikl za účelem vývoje Maclispu, vytyčil, patřila především snaha o návrh standardizované podoby programovacího jazyka Lisp; navíc se mělo jednat o vylepšení předchozích dialektů LISPu. Tento cíl se ovšem do značné míry podobá cílům dalších projektů, které vznikaly ve zhruba stejnou dobu: ZetaLisp, NIL, Spice Lisp popř. S-1 Lisp. Common Lisp vznikl na základě myšlenek a jazykových konstrukcí, které se v těchto dialektech Lispu objevily – proto také Common Lisp není, minimálně co se týká standardní knihovny tohoto jazyka, zcela konzistentní (což je více než patrné v porovnání se Scheme či Clojure).

Poznámka: taktéž nesmíme zapomenout na to, že za vývojem Common Lispu stálo relativně velké množství autorů i pracovních skupin (typicky realizovaných v rámci univerzitního prostředí napojeného na tehdy významné IT firmy), na rozdíl od (například) Scheme či dokonce Clojure, což je projekt, který vznikl jako „one man show“ (a dodnes je do značné míry ovlivněn především Richem Hickeym).
lisp01

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.

Vraťme se však k historii Common Lispu. V roce 1981 se začala tvořit kostra specifikace tohoto jazyka, přičemž za jejím vývojem stál především Bob Engelmore, v té době pracující v ARPA. Bobovým cílem bylo vytvoření standardu, který by však nebyl specifikován nějakou komisí, ale spíše komunitou (ovšem komunitou v dobovém významu toho slova – tedy nejvíce lidmi z univerzitního prostředí). A již o rok později, tedy v roce 1982, byla tehdy první verze specifikace prezentována známým Guy Lewisem Steelem Jr. (mj. spoluautorem jazyka Scheme) na konferenci ACM (ACM symposium on functional programming and LISP). Poté již práce na Common Lispu pokračovala v poměrně rychlém tempu (s ohledem na to, že se jednalo o jazyk, který se stal, slovy tvůrců „součástí přírody“ a již se pravděpodobně nebude a nemusí příliš měnit), takže v roce 1984 mohla vyjít první kniha o tomto jazyku – Common Lisp the Language – která byla současně i jeho poloformální specifikací.

Obrázek 4: Vývoj některých dialektů Lispu.
Zdroj: Wikipedia.

Ovšem z pohledu standardizace je důležitý i rok 1994, kdy vznikl standard ANSI Common Lisp. Po vydání této specifikace se vlastně (minimálně oficiálně) Common Lisp dále neměnil, což ovšem neznamená, že nedocházelo k postupnému rozšiřování, typicky formou knihoven maker a funkcí. Takto například vznikla podpora pro Unicode, I/O operace založené na CLOS či podpora pro souběžný běh výpočtů. V praxi to znamená mj. to, že zdrojové kódy z roku 1994 budou s velkou pravděpodobností použitelné i dnes (pochopitelně pokud nezávisí na nějakých zastaralých knihovnách či operačních systémech). Ostatně je zajímavé (a pro ekosystém jazyka Common Lisp možná i typické), že existuje relativně velké množství knihoven, které nebyly například posledních pět či dokonce deset let změněny („vylepšeny“) a přece jsou bez problému použitelné. Tato stabilita je v některých jiných ekosystémech prakticky nemyslitelná – ostatně vývojáři by se pravděpodobně na pět let nemodifikovanou knihovnu pro (například) Python či JavaScript dívali skrz prsty.

Poznámka: dnes je Common Lisp společně se Scheme, Racketem a Clojure nejpoužívanějším dialektem Lispu (zařadit sem můžeme ještě ELisp z Emacsu). Pro programátory může být užitečná tato tabulka, která některé výše uvedené dialekty porovnává: http://hyperpolyglot.org/lisp

3. Interaktivní vývoj – skutečný REPL a koncept „obrazu“ prostředí

„ Part of what makes Lisp distinctive is that it is designed to evolve. As new abstractions become popular (object-oriented programming, for example), it always turns out to be easy to implement them in Lisp. Like DNA, such a language does not go out of style.“
Paul Graham

Základem vývoje v (Common) LISPu je interaktivní prostředí založené na REPLu, tedy na okamžitém vyhodnocování zapsaných výrazů s výpisem jejich výsledků. Ovšem v případě Common Lispu je REPL navíc doplněn o debugger popsaný níže. V krátkosti – při vzniku chyby se neprovádí „rozbalovací“ fáze založená na zpětném procházení zásobníkových rámců. Tato operace je ponechána na uživateli, který tak může skutečně zkoumat „živý“ program.

Navíc Common Lisp dokáže pracovat s perzistentními objekty uloženými v tzv. obrazu (image), což je podobný koncept, jaký nalezneme například ve Smalltalku. Touto zajímavou technologií se budeme zabývat příště (dnes je tato technologie díky perzistentním databázím méně důležitá, než v osmdesátých letech, kdy byl tento koncept rozvinut).

Obrázek 5: Základem smyčky REPL je funkce EVAL. Zde je uveden prakticky celý její zdrojový kód získaný z dvoustránkové publikace o LISPu napsané McCarthym. Na první straně je popsán celý jazyk (jeho syntaxe i sémantika), stranu druhou zabírá právě výpis funkce EVAL.

4. Instalace Common Lispu a nastavení prostředí

Jak již víme z předchozích kapitol, je nutné Common Lisp chápat jako přesnou specifikaci jazyka a knihoven, nikoli jako konkrétní implementaci. Pro Linux lze využít více implementací Common Lispu, přičemž pravděpodobně nejpoužívanější je CMU Common Lisp zkracovaný na CMUCL a taktéž Steel Bank Common Lisp neboli zkráceně SBCL. Existuje však například i GNU Common Lisp atd. V této kapitole si ukážeme instalaci SBCL, protože právě balíček s touto variantou Common Lispu naleznete v repositářích mnoha distribucí Linuxu.

Instalace na systémy založené na balíčcích RPM (Fedora, RHEL):

# dnf install sbcl
 
Last metadata expiration check: 0:25:24 ago on Fri 28 Jan 2022 07:36:45 AM EST.
Dependencies resolved.
================================================================================
 Package                Arch   Version           Repository                Size
================================================================================
Installing:
 sbcl                   x86_64 2.0.1-5.fc34      beaker-Fedora-Everything  15 M
Installing dependencies:
 cl-asdf                noarch 20101028-19.fc34  beaker-Fedora-Everything  87 k
 common-lisp-controller noarch 7.4-21.fc34       beaker-Fedora-Everything  24 k
 
Transaction Summary
================================================================================
Install  3 Packages
 
Total download size: 16 M
Installed size: 73 M
Is this ok [y/N]:
 
Downloading Packages:
(1/3): common-lisp-controller-7.4-21.fc34.noarc 1.6 MB/s |  24 kB     00:00
(2/3): cl-asdf-20101028-19.fc34.noarch.rpm      838 kB/s |  87 kB     00:00
(3/3): sbcl-2.0.1-5.fc34.x86_64.rpm              24 MB/s |  15 MB     00:00
--------------------------------------------------------------------------------
Total                                            24 MB/s |  16 MB     00:00
Running transaction check
Transaction check succeeded.
Running transaction test
Transaction test succeeded.
Running transaction
  Preparing        :                                                        1/1
  Installing       : cl-asdf-20101028-19.fc34.noarch                        1/3
  Installing       : common-lisp-controller-7.4-21.fc34.noarch              2/3
  Installing       : sbcl-2.0.1-5.fc34.x86_64                               3/3
  Running scriptlet: sbcl-2.0.1-5.fc34.x86_64                               3/3
  Verifying        : cl-asdf-20101028-19.fc34.noarch                        1/3
  Verifying        : common-lisp-controller-7.4-21.fc34.noarch              2/3
  Verifying        : sbcl-2.0.1-5.fc34.x86_64                               3/3
 
Installed:
  cl-asdf-20101028-19.fc34.noarch   common-lisp-controller-7.4-21.fc34.noarch
  sbcl-2.0.1-5.fc34.x86_64
 
Complete!

Instalace na systémy založené na balíčcích Debianu:

# sudo apt-get install sbcl
 
Reading package lists... Done
Building dependency tree
Reading state information... Done
Suggested packages:
  sbcl-doc sbcl-source slime
The following NEW packages will be installed:
  sbcl
0 upgraded, 1 newly installed, 0 to remove and 2 not upgraded.
Need to get 0 B/8 439 kB of archives.
After this operation, 43,3 MB of additional disk space will be used.
Selecting previously unselected package sbcl.
(Reading database ... 291609 files and directories currently installed.)
Preparing to unpack .../sbcl_2%3a2.0.1-3_amd64.deb ...
Unpacking sbcl (2:2.0.1-3) ...
Setting up sbcl (2:2.0.1-3) ...
Processing triggers for man-db (2.9.1-1) ...

Prostředí SBCL se spustí následovně:

$ sbcl
 
This is SBCL 2.0.1.debian, an implementation of ANSI Common Lisp.
More information about SBCL is available at <http://www.sbcl.org/>.
 
SBCL is free software, provided as is, with absolutely no warranty.
It is mostly in the public domain; some portions are provided under
BSD-style licenses.  See the CREDITS and COPYING files in the
distribution for more information.
*
Poznámka: ona hvězdička na konci (přesněji řečeno na samostatném řádku) je výzva (prompt), která se může v závislosti na kontextu změnit.

SBCL není (alespoň většinou) slinkován s knihovnami typu readline. Proč je to však tak důležité, že se o tom explicitně zmiňujeme? Nepodpora readline, mj. znamená, že neexistuje historie příkazového řádku, není možné používat editační příkazy typu Ctrl+A, Ctrl+E, ani vyhledávání v dříve zadaných příkazech pomocí Ctrl+R atd. A navíc nefunguje automatické doplňování jmen symbolů klávesou Tab. Tyto vlastnosti, které dnes od REPLů prakticky automaticky očekáváme, je možné do jisté míry doplnit externím nástrojem rlwrap. Tomu můžeme v případě potřeby předat soubor se symboly pro automatické doplňování. Nástroj rlwrap se instaluje snadno:

$ dnf install rlwrap
 
Last metadata expiration check: 0:33:16 ago on Sat 09 Apr 2022 06:45:42 AM EDT.
Dependencies resolved.
================================================================================
 Package              Arch        Version              Repository          Size
================================================================================
Installing:
 rlwrap               x86_64      0.45.2-1.fc34        updates            132 k
Installing dependencies:
 perl-File-Slurp      noarch      9999.32-3.fc34       beaker-Fedora       31 k
 perl-lib             x86_64      0.65-477.fc34        updates             25 k
 
Transaction Summary
================================================================================
Install  3 Packages
 
Total download size: 188 k
Installed size: 399 k
Is this ok [y/N]: y
Downloading Packages:
(1/3): perl-File-Slurp-9999.32-3.fc34.noarch.rp 3.6 MB/s |  31 kB     00:00
(2/3): perl-lib-0.65-477.fc34.x86_64.rpm        197 kB/s |  25 kB     00:00
(3/3): rlwrap-0.45.2-1.fc34.x86_64.rpm          649 kB/s | 132 kB     00:00
--------------------------------------------------------------------------------
Total                                           142 kB/s | 188 kB     00:01

S využitím rlwrap se bude prostředí SBCL spouštět následujícím způsobem:

$ rlwrap sbcl

Se (zdánlivě) stejným výsledkem, ovšem v mnoha ohledech s vylepšenou příkazovou řádkou:

This is SBCL 2.0.1-5.fc34, an implementation of ANSI Common Lisp.
More information about SBCL is available at <http://www.sbcl.org/>.
 
SBCL is free software, provided as is, with absolutely no warranty.
It is mostly in the public domain; some portions are provided under
BSD-style licenses.  See the CREDITS and COPYING files in the
distribution for more information.
*
Poznámka: ve skutečnosti je však REPL Common Lispu (resp. SBCL) většinou volán z integrovaného vývojového prostředí nebo z programátorského textového editoru.

5. Interní debugger Common Lispu

Důležitou součástí SBCL je i interní debugger, který umožňuje jak detekci problémů, tak i interaktivní ladění a opravu kódu. Debugger je spuštěn buď při vzniku chyby či výjimky, nebo například zavoláním funkce cerror. Podívejme se na první možnost. Do prostředí SBCL zapíšeme neznámý identifikátor (resp. symbol):

* foobar

Namísto pouhého konstatování, že došlo k chybě, se spustí debugger, který uživateli nabídne několik možností:

debugger invoked on a UNBOUND-VARIABLE in thread
#<THREAD "main thread" RUNNING {1000510083}>:
  The variable FOOBAR is unbound.
 
Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.
 
restarts (invokable by number or by possibly-abbreviated name):
  0: [CONTINUE   ] Retry using FOOBAR.
  1: [USE-VALUE  ] Use specified value.
  2: [STORE-VALUE] Set specified value and use it.
  3: [ABORT      ] Exit debugger, returning to top level.
 
(SB-INT:SIMPLE-EVAL-IN-LEXENV FOOBAR #<NULL-LEXENV>)
0]

Odlišně se debugger zachová v případě, že se pokusíme zavolat neznámou funkci:

* (foobar)
; in: FOOBAR
;     (FOOBAR)
;
; caught STYLE-WARNING:
;   undefined function: COMMON-LISP-USER::FOOBAR
;
; compilation unit finished
;   Undefined function:
;     FOOBAR
;   caught 1 STYLE-WARNING condition
 
debugger invoked on a UNDEFINED-FUNCTION in thread
#<THREAD "main thread" RUNNING {1000510083}>:
  The function COMMON-LISP-USER::FOOBAR is undefined.
 
Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.
 
restarts (invokable by number or by possibly-abbreviated name):
  0: [CONTINUE      ] Retry calling FOOBAR.
  1: [USE-VALUE     ] Call specified function.
  2: [RETURN-VALUE  ] Return specified values.
  3: [RETURN-NOTHING] Return zero values.
  4: [ABORT         ] Exit debugger, returning to top level.
 
("undefined function")

Zajímavá je možnost RETURN-VALUE, která umožňuje pokračovat ve (složitém) výpočtu nahrazením volání funkce za uživatelem zadanou hodnotu. Například můžete spustit program, který má komunikovat se specifickou a nedostupnou službou atd.

Příklad vyvolání debuggeru programově, tj. zavoláním funkce cerror:

(defun factorial (n)
  (if (minusp n) (cerror "continue with negative value?" "negative value ~s" n))
  (* n (factorial (- n 1))))

Spustíme SBCL a načteme do něj zdrojový kód této funkce:

$ sbcl --load broken_factorial.lisp 
 
This is SBCL 2.0.1.debian, an implementation of ANSI Common Lisp.
More information about SBCL is available at <http://www.sbcl.org/>.
 
SBCL is free software, provided as is, with absolutely no warranty.
It is mostly in the public domain; some portions are provided under
BSD-style licenses.  See the CREDITS and COPYING files in the
distribution for more information.

Po dosažení záporného n dojde k chybě a vyvolání debuggeru:

* (factorial 10)
 
debugger invoked on a SIMPLE-ERROR in thread
#<THREAD "main thread" RUNNING {1000510083}>:
  negative value -1
 
Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.
 
restarts (invokable by number or by possibly-abbreviated name):
  0: [CONTINUE] continue with negative value?
  1: [ABORT   ] Exit debugger, returning to top level.
 
(FACTORIAL -1)
   source: (CERROR "continue with negative value?" "negative value ~s" N)
Poznámka: povšimněte si, že se vypíšou přesné informace o chybě – ta je v Common Lispu typicky představována textovou zprávou a nikoli objektem.

6. První krůčky s Common Lispem

V této kapitole se ve stručnosti seznámíme se základními vlastnostmi LISPu, ovšem s ohledem na to, že již známe způsob programování ve Scheme a Clojure (viz odkazy uvedené v osmácté kapitole), takže je možné se zaměřit na největší rozdíly. Ještě předtím se musíme alespoň ve stručnosti seznámit s různými variantami funkce print, protože tyto varianty budeme používat v demonstračních příkladech. V následující tabulce je vypsána většina funkcí, které lze použít pro zobrazení nějakých hodnot uživateli. Dnes využijeme funkce nazvané princ, terpri a v některých příkladech i funkci format (což je obdoba printf):

Funkce Stručný popis
print tisk hodnoty takovým způsobem, aby ji bylo možné načíst zpět pomocí read
prin1 dtto, ovšem bez konce řádku
princ tisk hodnoty tak, aby byla dobře čitelná uživatelem (nekompatibilní s read)
terpri odřádkování (terminate print)
format naformátování zprávy podle zadané šablony a její tisk či vrácení ve formě řetězce

V programovacím jazyce LISP je možné kromě základních (interních, primitivních) funkcí definovat a následně i volat funkce uživatelské, podobně jako v mnoha dalších programovacích jazycích. Ve skutečnosti je velká část programů napsaných v LISPu tvořena právě definicemi nových funkcí vystavěných na základě standardních funkcí a několika speciálních forem. V Common Lispu, což je jedna z nejrozšířenějších a také nejkomplexnějších implementací tohoto jazyka, se funkce vytváří pomocí speciální formy nazvané defun (define function). Formát volání formy defun při tvorbě nové funkce je velmi jednoduchý, programátor si ovšem musí dát pozor na správné uzávorkování:

(defun název_funkce(parametry funkce) tělo funkce)

Návratovou hodnotou nově vytvořené funkce (po jejím zavolání a vykonání) je hodnota výrazu tvořícího tělo funkce, což znamená, že není nutné používat nějakou formu příkazu return tak, jak je tomu v mnoha dalších programovacích jazycích. Ukažme si nyní způsob vytvoření dvou jednoduchých funkcí a následného zavolání těchto funkcí:

; funkce vracející druhou mocninu svého jediného parametru
(defun sqr(x) (* x x))
 
; funkce, která sečte hodnoty svých dvou parametrů a vrátí výsledek součtu
(defun plus(x y) (+ x y))
 
; zavoláme první funkci
(sqr 42)
1764
 
; a nyní funkci druhou
(plus 2 3)
5
 
; funkce lze samozřejmě libovolným způsobem kombinovat
(plus (sqr 3) (sqr 4))
25
 
; podporovány jsou i zlomky
(plus 1/2 2/3)
7/6
Poznámka: existuje i možnost definice funkce s nepovinnými parametry atd., což je téma navazujícího článku. Taktéž je dobré upozornit na to, že ve Scheme se namísto defun používá define s odlišným stylem zápisu a v Clojure defn.

Common Lisp patří mezi ty dialekty Lispu, které pokládají rovnítko mezi hodnotou nil a prázdným seznamem (což jsou zcela ekvivalentní hodnoty):

nil
NIL
 
'()
NIL
 
()
NIL

Globální proměnné je vhodné před jejich použitím deklarovat:

(defvar x 1)

Teprve poté lze měnit jejich hodnotu, například přes setf (zde se Common Lisp liší například od Elispu):

(setf x 2)

Pokus o nastavení nedefinované proměnné vede k zobrazení varování:

* (setf y 10)
 
; in: SETF Y
;     (SETF Y 10)
; ==>
;   (SETQ Y 10)
;
; caught WARNING:
;   undefined variable: COMMON-LISP-USER::Y
;
; compilation unit finished
;   Undefined variable:
;     Y
;   caught 1 WARNING condition
10

Definice konstanty:

(defconstant A 42)

Typová kontrola v době překladu (tomuto zajímavému tématu bude věnován samostatný článek):

* (defun foo () (concatenate 'string "+" A))
 
; in: DEFUN FOO
;     (CONCATENATE 'STRING "+" A)
;
; caught WARNING:
;   Constant 42 conflicts with its asserted type SEQUENCE.
;   See also:
;     The SBCL Manual, Node "Handling of Types"
;
; compilation unit finished
;   caught 1 WARNING condition
FOO

7. Přerušení výpočtu a jeho obnovení

Debugger, který je součástí většiny implementací Common Lispu, dokáže zareagovat i na běhovou chybu. Ovšem nikoli tak, že by pouze vypsal obsah zásobníkových rámců (což je například chování Pythonu i JVM – kde se tak možnost ladění do jisté míry ztratí). Naopak – k „rozvinovací“ (unwinding) fázi nedojde, resp. přesněji řečeno se Common Lisp zeptá programátora, jakou operaci má konkrétně provést. Například je možné kód opravit a spustit znovu, a to přesně z toho místa, kde byl běh přerušen (tedy s využitím původního obsahu zásobníkových rámců).

Podívejme se na konkrétní, velmi jednoduchý příklad. Jedná se o klasický rekurzivní výpočet faktoriálu, ovšem používá se zde neznámý symbol default, který není nikde nastaven:

(defun factorial (n)
    (cond ((zerop n) default)
          (T (* n (factorial (- n 1))))))

Pokusme se nyní tento zdrojový kód načíst do SBCL:

$ sbcl --load broken_factorial2.lisp 

V průběhu načítání se již dopředu vypíše varování a nedefinované proměnné. Ovšem kód se načte, neboť Lisp předpokládá, že se proměnná může (na globální úrovni) kdykoli později objevit, a to ještě před zavoláním funkce factorial (bylo by ostatně zajímavé tento koncept přidat i do dalších podobně koncipovaných jazyků):

This is SBCL 2.0.1.debian, an implementation of ANSI Common Lisp.
More information about SBCL is available at <http://www.sbcl.org/>.
 
SBCL is free software, provided as is, with absolutely no warranty.
It is mostly in the public domain; some portions are provided under
BSD-style licenses.  See the CREDITS and COPYING files in the
distribution for more information.
 
; file: /home/ptisnovs/src/lisp-families/common-lisp/broken_factorial2.lisp
; in: DEFUN FACTORIAL
;     (COND ((ZEROP N) DEFAULT) (T (* N (FACTORIAL (- N 1)))))
; ==>
;   (IF (ZEROP N)
;       DEFAULT
;       (THE T (* N (FACTORIAL (- N 1)))))
;
; caught WARNING:
;   undefined variable: COMMON-LISP-USER::DEFAULT
;
; compilation unit finished
;   Undefined variable:
;     DEFAULT
;   caught 1 WARNING condition

Nyní se pokusme funkci factorial zavolat:

* (factorial 10)

Podle očekávání dojde k chybě, ovšem debugger nám umožňuje pokračovat ve výpočtu zadáním specifikované hodnoty, která se za default dosadí:

debugger invoked on a UNBOUND-VARIABLE in thread
#<THREAD "main thread" RUNNING {1000560083}>:
  The variable DEFAULT is unbound.
 
Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.
 
restarts (invokable by number or by possibly-abbreviated name):
  0: [CONTINUE   ] Retry using DEFAULT.
  1: [USE-VALUE  ] Use specified value.
  2: [STORE-VALUE] Set specified value and use it.
  3: [ABORT      ] Exit debugger, returning to top level.
 
(FACTORIAL 0)
   source: (COND ((ZEROP N) DEFAULT) (T (* N (FACTORIAL (- N 1)))))

Zkusme tedy vybrat možnost číslo 1 a dosadit hodnotu default:

0] 1
 
Enter a form to be evaluated: 1
3628800
Poznámka: důležité je si uvědomit, že po zadání hodnoty došlo k fázi rozbalení při výpočtu faktoriálu, tj. desetkrát se provedl výpočet n * factorial(n-1) a nakonec jsme dostali očekávaný výsledek.

8. Multiparadigmatický programovací jazyk

O Lispu (resp. někdy dokonce přímo o Common Lispu) se v některých dokumentech tvrdí, že se jedná o funkcionální jazyk. Ve skutečnosti je Common Lisp spíše multiparadigmatickým jazykem, který mj. umožňuje vytvářet programy s využitím metodiky funkcionálního programování (funkce, funkce vyššího řádu, uzávěry, neměnitelné hodnoty, striktně lokální proměnné). Ovšem stejně dobře je možné použít klasický imperativní styl (měnitelné seznamy, globální proměnné, …), objektově orientovaný styl (typicky s využitím CLOS – což je téma na samostatný článek) a především se Common Lisp používá i jako metajazyk. Navíc je možné do LISPu přidávat další doménově specifické jazyky nebo i například do něho „vložit“ další programovací jazyk (příkladem tohoto typu je projekt April, což je vlastně APL dostupné přímo z LISPu).

Ostatně podívejme se na následující zcela legální kód napsaný pro Common Lisp, který se podobá starému (dobrému?) BASICu s čísly řádků a skoky vytvořenými s využitím go (v BASICu pomocí GOTO):

(tagbody
  10 (print "Hello!")
  20 (sleep 2)
  30 (go 10))
Poznámka: toto není, i když by se to mohlo tak jevit, zcela umělý příklad, protože tagbody atd. nalezneme v expanzi mnoha maker, například i ve smyčce loop popsané dále.

9. Makrosystém Common Lispu

Jednou z nejzajímavějších vlastností LISPu (přesněji řečeno některých jeho implementací, jejichž typickým zástupcem je i Common Lisp) je možnost tvorby maker. Vzhledem k tomu, že LISPovské programy jsou tvořeny, stejně jako data, pomocí rekurzivně vnořených seznamů, jsou LISPovská makra založena na manipulaci se seznamy tvořícími program, což je velký rozdíl například oproti makrům implementovaným v céčkovém preprocesoru, kde se jedná o poměrně jednoduché (a z pohledu programátora značně primitivní) textové záměny. Vzhledem k tomu, že LISPovská makra dokážou manipulovat s vlastním programem, je možné pomocí nich vytvářet například úplně nové jazykové konstrukce (různé smyčky, podmíněné příkazy, částečně vyhodnocované formy atd.) s vlastní syntaxí, což je poměrně unikátní vlastnost, kterou u většiny dalších programovacích jazyků nenajdeme. Způsob definice maker se v některých ohledech podobá definici funkcí, ale mezi funkcemi a makry existuje jeden zásadní rozdíl.

LISPovské funkce získávají jako svoje argumenty LISPovské hodnoty, tj. většinou atomy, (anonymní) funkce nebo seznamy, a vrací taktéž nějakou LISPovskou hodnotu – opět se může jednat o atom, (anonymní) funkci nebo seznam. Funkce jsou vyhodnocovány (volány) až při spuštění programu. Makra jako svůj vstup získávají LISPovský kód (zapsaný formou rekurzivně zanořeného seznamu) a vrací taktéž LISPovský kód, což nepředstavuje oproti funkcím žádný zásadnější rozdíl. Ovšem na rozdíl od funkcí jsou makra volána již při prvotním zpracovávání programu, podobně jako jsou céčková makra zpracovávána céčkovým preprocesorem (cpp) ještě před vlastní kompilací. Teprve výsledek volání makra (nazývaný taktéž expanze makra) je považován za zápis výrazu, který může být dále zpracován, tj. buď vyhodnocen (interpretační varianty LISPu) nebo zkompilován (varianty LISPu vybavené překladačem). Poznamenejme ještě, že v těle makra se může vyskytovat volání dalšího makra, což znamená, že LISP musí při expanzi maker použít rekurzi.

Poznámka: makry se budeme podrobněji zabývat příště.

10. LISP jako jazyk pro tvorbu (doménově specifických) jazyků

Doménově specifické jazyky (anglicky DSL neboli Domain-Specific Language) jsou velmi důležitou součástí informatiky a mnohé z nich jsou velmi úspěšné a rozšířené do mnoha oblastí. Za připomenutí stojí například jazyk pro popis regulárních výrazů, jenž je podporován jak mnoha nástroji, tak i knihovnami, popř. je přímo součástí některých obecných programovacích jazyků (Perl apod.). I v některých dalších případech je tento přístup velmi užitečný a rozšířený, ostatně například SQL je s velkou pravděpodobností nejpopulárnějším samostatně chápaným doménově specifickým jazykem neboli DSL vůbec, protože umožňuje snadné optimalizace dotazů a vůbec pokládání dotazů čitelným, pochopitelným a přenositelným způsobem. Dalším doménově specifickým jazykem, s nímž jsme se již na stránkách Roota v rámci několika článků seznámili, je jazyk Gherkin určený pro popis chování systémů a pro psaní BDD testů. Dalším příkladem je PostScript.

Mnohé z doménově specifických jazyků nejsou Turingovsky kompletní (úplný), což však není nedostatek, ale mnohdy naopak požadovaná vlastnost. Příkladem mohou být DSL, v nichž není možné zapsat programové smyčky ani rekurzi – tudíž je většinou výpočet resp. vyhodnocení výrazu časově dosti přesně určené. Další DSL neumožňují explicitní alokaci paměti, což může být v dané oblasti použití taktéž výhodné. Nasazení DSL oproti plnohodnotnému jazyku tedy může být výhodné, protože cíleně omezené možnosti takového jazyka můžeme chápat jako formu „sémantického sandboxingu“ (právě proto jsou regulární výrazy regulární).

Poznámka: na tomto místě je zajímavé zmínit PostScript, který je sice DSL, konkrétně specifickým jazykem pro popis tiskových stran, ovšem oproti mnoha jiným DSL je Turingovsky kompletní. To například umožňuje vykreslování procedurální grafiky (viz například tyto příklady), ovšem pokud tyto zcela korektní PostScriptové soubory spustíte na podnikové tiskárně, můžete se dočkat nemilého překvapení v několikahodinové odstávce, popř. nepříjemného e-mailu z IT oddělení :-)

Právě LISP a tím pádem i Common Lisp, je velmi vhodným jazykem pro tvorbu doménově specifických jazyků, a to z toho důvodu, že lze relativně snadno manipulovat s abstraktním syntaktickým stromem (s využitím maker) ještě před vlastním zpracováním kódu ve funkci eval. A příkladů DSL vytvořených v LISPu existuje poměrně velké množství. V navazujících kapitolách se seznámíme s makrem loop, které do Common Lispu přidává DSL pro tvorbu různých programových smyček, a to mnohdy i značně složitých:

(loop repeat 1000
      for x = (random 100)
      if (evenp x)
        collect x into evens
      else
        collect x into odds
      finally (return (values evens odds)))

Dále knihovna spinneret umožňuje zápis struktury HTML stránky přímo v LISPu, pochopitelně s možností použití maker atd.:

(let ((*html-style* :human))
  (with-html
    (:div
      (:p "Text " (:a "link text") " more text"))))

Z dalších známějších DSL můžeme jmenovat i SxQL pro zápis dotazů do databáze:

(select (:title :author :year)
  (from :books)
  (where (:and (:>= :year 1995)
               (:< :year 2010)))
  (order-by (:desc :year)))
Poznámka: síla těchto DSL se projeví zejména ve chvíli, kdy je zapotřebí dynamicky předávat parametry, proměnný počet parametrů atd. (kdo by taky chtěl neustále skládat SQL příkazy ručně že?)

11. DSL pro tvorbu programových smyček – makro loop

Smyčka loop ve formě, v jaké byla navržena v Common Lispu (a poté částečně převzata do Elispu), programátorům nabízí svůj vlastní doménově specifický jazyk (DSL). Z dalších demonstračních příkladů bude patrné, že tento jazyk používá styl zápisu, který je kombinací klasických strukturovaných jazyků (Algol, Pascal, C) a možností LISPu. Je tomu tak z toho důvodu, aby bylo přímo ze zápisu smyčky, typicky již po přečtení prvního řádku, patrné, jak bude smyčka prováděna. K tomuto účelu se uvnitř smyčky loop používají symboly for, repeat, in, finally atd., které mají svůj speciální význam, ale pouze uvnitř samotné formy loop. Tím se loop do značné míry liší od smyček v klasických jazycích, které programátory „nutí“ realizovat kód smyčky v jejím těle, což nemusí být vždy idiomatické.

Poznámka: v Common Lispu je možné tyto symboly zapisovat i ve formě keywordů, tj. s dvojtečkami na začátku, což může být čitelnější.

Podívejme se nyní na pravděpodobně nejjednodušší prakticky použitelný příklad využívající smyčku loop, v níž bude použita dvojice symbolů se speciálním významem, o nichž jsme se zmínili v předchozím odstavci. Konkrétně budeme implementovat programovou smyčku, jejíž tělo se bude n-krát opakovat. K zápisu této varianty smyčky nám pomohou dva symboly se jmény repeat a do. Povšimněte si, že zápis smyčky vypadá prakticky stejně, jako by tomu bylo v některém z klasických strukturovaných jazyků (samozřejmě pokud si odmyslíme kulaté závorky, do kterých toto makro vkládáme a které jsou v Lispu v této formě povinné):

(loop repeat 10 do
      (princ "Hello world!")
      (terpri))

Makro loop zavolané tímto způsobem nevrací žádnou hodnotu, takže se předpokládá, že smyčka vykoná svoji činnost jen díky tomu, že některá funkce volaná při každé iteraci bude mít vedlejší efekt, například že vypíše zprávu na obrazovku atd., což je ostatně přesně náš případ. Po spuštění výše popsané smyčky se na výstupu skutečně zobrazí deset totožných zpráv:

Hello world!
Hello world!
Hello world!
Hello world!
Hello world!
Hello world!
Hello world!
Hello world!
Hello world!
Hello world!
Poznámka: povšimněte si, že při použití loop-repeat-do vlastně nemáme k dispozici počitadlo smyčky. V případě, že je nutné počitadlo využít, je výhodnější použít další varianty smyčky, například loop-for, které budou popsány dále.

12. Použití klauzulí while a until

Ve smyčce loop je možné v případě potřeby použít i klauzule while a until, za nimiž se zapisuje podmínka. Ta je od těla smyčky oddělena speciálním symbolem do, takže zápisy vypadají následovně:

Vyhodnocení podmínky na začátku každé iterace, tělo smyčky se zavolá, pokud je podmínka splněna:

(loop while podmínka do ...)

Vyhodnocení podmínky na začátku každé iterace, tělo smyčky se zavolá, pokud podmínka splněna naopak není (po splnění podmínky se smyčka opustí):

(loop until podmínka do ...)

Demonstrační příklad bude velmi jednoduchý, protože v něm použijeme jedinou řídicí proměnnou i, kterou budeme nejprve zmenšovat o jedničku a poté v druhé smyčce naopak zvyšovat až do chvíle, kdy překročí hodnotu 10:

(defvar i 10)
 
(loop while (> i 0) do
      (format T "i = ~d~%" i)
      (setq i (- i 1)))
 
(princ "rebound")
(terpri)
 
(loop until (> i 10) do
      (format T "i = ~d~%" i)
      (setq i (+ i 1)))

Výsledek bude vypadat takto:

i = 10
i = 9
i = 8
i = 7
i = 6
i = 5
i = 4
i = 3
i = 2
i = 1
rebound
i = 0
i = 1
i = 2
i = 3
i = 4
i = 5
i = 6
i = 7
i = 8
i = 9
i = 10

13. Různé varianty počítané smyčky typu for

Makro loop samozřejmě podporuje i tvorbu klasických programových smyček, v nichž se postupně mění hodnota počitadla. Základní forma této smyčky vypadá následovně:

(loop for i to 10 do
      (format T "i = ~d~%" i))

Výsledek ukazuje, že se počítá od nuly a končí se až po dosažení koncové hodnoty (pozor – ne o jedničku dříve):

i = 0
i = 1
i = 2
i = 3
i = 4
i = 5
i = 6
i = 7
i = 8
i = 9
i = 10

Alternativně můžeme v tomto případě namísto symbolu to použít spíše upto:

(loop for i upto 10 do
      (format T "i = ~d~%" i))

A to se shodným výsledkem:

i = 0
i = 1
i = 2
i = 3
i = 4
i = 5
i = 6
i = 7
i = 8
i = 9
i = 10

Mnohdy potřebujeme, aby horní meze nebylo dosaženo a smyčka skončila těsně předtím (což do určité míry odpovídá Pythonovskému for i in range(x,y)). Namísto komplikovaných výpočtů použijte below a nikoli to či upto:

(loop for i below 10 do
      (format T "i = ~d~%" i))

V tomto případě se skutečně počítá jen do 9 a nikoli 10:

i = 0
i = 1
i = 2
i = 3
i = 4
i = 5
i = 6
i = 7
i = 8
i = 9

Užitečná je možnost přidání další (libovolné) podmínky, která pro počitadlo musí platit. Jsou přeskočeny ty iterace, kdy podmínka není splněna (ovšem smyčka není ukončena):

(loop for i below 10 when (evenp i) do
      (format T "i = ~d~%" i))

Výsledkem je v tomto případě sekvence sudých čísel:

i = 0
i = 2
i = 4
i = 6
i = 8

Popř. naopak:

(loop for i below 10 when (oddp i) do
      (format T "i = ~d~%" i))

Výsledkem je nyní sekvence čísel lichých:

i = 1
i = 3
i = 5
i = 7
i = 9

Samozřejmě je možné v případě potřeby specifikovat i počáteční hodnotu počitadla; tj. nemusí se vždy začínat na nule:

(loop for i from 1 to 10 do
      (format T "i = ~d~%" i))

Výsledek:

i = 1
i = 2
i = 3
i = 4
i = 5
i = 6
i = 7
i = 8
i = 9
i = 10

Totožná smyčka, ovšem s jiným symbolem (lépe čitelným):

(loop for i from 1 upto 10 do
      (format T "i = ~d~%" i))

Výsledek je stejný:

i = 1
i = 2
i = 3
i = 4
i = 5
i = 6
i = 7
i = 8
i = 9
i = 10

Kombinace from a below:

(loop for i from 1 below 10 do
      (format T "i = ~d~%" i))

Výsledek je kratší o poslední iteraci:

i = 1
i = 2
i = 3
i = 4
i = 5
i = 6
i = 7
i = 8
i = 9

Další kombinace již obsahují klauzuli when:

(loop for i from 1 to 10 when (evenp i) do
      (format T "i = ~d~%" i))

Výsledek:

i = 2
i = 4
i = 6
i = 8
i = 10
(loop for i from 1 upto 10 when (evenp i) do
      (format T "i = ~d~%" i))

Výsledek:

i = 2
i = 4
i = 6
i = 8
i = 10
(loop for i upfrom 1 upto 10 when (evenp i) do
         (princ (format "i = %d\n" i)))

Výsledek:

i = 2
i = 4
i = 6
i = 8
i = 10
(loop for i from 1 below 10 when (evenp i) do
         (princ (format "i = %d\n" i)))

Výsledek:

i = 2
i = 4
i = 6
i = 8

Další demonstrační příklad si již ukážeme v celku. Používá se v něm symbol by, za nímž se udává krok, tj. o jakou hodnotu se bude počitadlo měnit. Výchozí hodnotou je pochopitelně jednička:

(loop for i from 0 to 30 by 3 do
         (princ (format "i = %d\n" i)))
 
(princ "---------------------------\n")
 
(loop for i from 0 upto 30 by 3 do
         (princ (format "i = %d\n" i)))
 
(princ "---------------------------\n")
 
(loop for i upfrom 0 upto 30 by 3 do
         (princ (format "i = %d\n" i)))
 
(princ "---------------------------\n")
 
(loop for i upfrom 0 below 30 by 3 do
         (princ (format "i = %d\n" i)))
 
(princ "===========================\n")
 
(loop for i from 0 to 30 by 3 when (evenp i) do
         (princ (format "i = %d\n" i)))
 
(princ "---------------------------\n")
 
(loop for i from 0 upto 30 by 3 when (evenp i) do
         (princ (format "i = %d\n" i)))
 
(princ "---------------------------\n")

Prozatím jsme v předchozích smyčkách počitadlo vždy zvyšovali, ať již o jedničku nebo o jinou hodnotu. Počítat je však možné i opačným směrem. V tomto případě však nestačí za by zadat záporné číslo! Je nutné použít jiný zápis, a to s využitím symbolů downto nebo above. Opět si ukažme některé povolené kombinace (pozor na rozdílné chování downto a above):

(loop for i from 10 downto 1 do
         (princ (format "i = %d\n" i)))
 
(princ "---------------------------\n")
 
(loop for i from 10 above 1 do
         (princ (format "i = %d\n" i)))
 
(princ "---------------------------\n")
 
(loop for i upfrom 10 above 1 do
         (princ (format "i = %d\n" i)))
 
(princ "---------------------------\n")
 
(loop for i from 10 downto 1 when (evenp i) do
         (princ (format "i = %d\n" i)))
 
(princ "---------------------------\n")
 
(loop for i from 10 above 1 when (evenp i) do
         (princ (format "i = %d\n" i)))

14. Procházení prvky seznamu aneb smyčka typu for-each

V případě, že se má procházet všemi prvky seznamu (nebo vektoru), lze použít klauzuli for in, která používá následující styl zápisu:

(loop for prvek in seznam
  ...
  ...
  ...
  tělo smyčky
  ...
  ...
  ...)

Vidíme, že se tento zápis opět do značné míry podobá syntaxi, s níž se setkáme v běžných programovacích jazycích. Typicky se tato smyčka používá ve chvíli, kdy se v ní volá funkce s vedlejším efektem. Pokud tomu tak není a je nutné ze smyčky vrátit výsledek aplikace nějaké funkce na prvky seznamu, používá se klauzule collect, a to přibližně tímto způsobem:

(loop for prvek in seznam
    collect prvek)

V následujícím příkladu je tato varianta smyčky použita, i když se ve skutečnosti dá nahradit funkcí mapcar:

(defvar lst '(1 2 3 4 5 6 7 8 9 10))
 
(print
    (loop for i in lst
          collect i))
 
(print
    (loop for i in lst
          collect (* i i)))
 
 
(defun factorial (n)
    (cond ((zerop n) 1)
          (T (* n (factorial (- n 1))))))
 
(print
    (loop for i in lst
          collect (factorial i)))

Výsledky:

(0 1 2 3 4 5 6 7 8 9 10)
 
(0 1 4 9 16 25 36 49 64 81 100)
 
(1 1 2 6 24 120 720 5040 40320 362880 3628800)

Existuje ještě jedna varianta smyčky for-each, ovšem tato varianta používá zápis se symbolem on a nikoli in:

(loop for prvek on seznam
    collect prvek)

Tato varianta prochází seznamem odlišně – v první iteraci se do řídicí proměnné smyčky vloží celý seznam, ve druhé iteraci seznam bez prvního prvku atd. atd.:

(defvar lst '(1 2 3 4 5 6 7 8 9 10))
 
(defvar result
   (loop for i on lst
          collect i))
 
(dolist (item result)
  (print item))

Výsledek nyní bude značně odlišný – bude se totiž jednat o seznam seznamů:

(0 1 2 3 4 5 6 7 8 9 10)
 
(1 2 3 4 5 6 7 8 9 10)
 
(2 3 4 5 6 7 8 9 10)
 
(3 4 5 6 7 8 9 10)
 
(4 5 6 7 8 9 10)
 
(5 6 7 8 9 10)
 
(6 7 8 9 10)
 
(7 8 9 10)
 
(8 9 10)
 
(9 10)
 
(10)

15. Vybrané další možnosti poskytované makrem loop

V předchozích demonstračních příkladech jsme pro získání výsledné hodnoty smyčky používali klauzuli collect. Použít je však možné i klauzuli append, která pracuje podobně, ale pokud této klauzuli předáme seznam, budou všechny jeho prvky přidány do výsledného seznamu (každý zvlášť). Můžeme tím tedy nahradit operaci flatten:

(defvar lst '(1 2 3 4 5 6 7 8 9 10))
 
(defvar result
        (loop for i in lst
              collect i))
 
(dolist (item result)
  (print item))
 
(terpri)
(princ "-------------------------------------")
 
(defvar kids '((alfa beta) () (gama delta) (omega) ()))
 
(setq result
      (loop for i in kids
            append i))
 
(dolist (item result)
  (print item))

S výsledkem:

1
2
3
4
5
6
7
8
9
10
-------------------------------------
ALFA
BETA
GAMA
DELTA
OMEGA

Podívejme se ještě na některé složitější konstrukce, které dokážeme s makrem loop vytvořit. Poměrně často se setkáme se situací, kdy je nutné vypočítat sumu všech prvků nějakého seznamu nebo vektoru. To lze provést několika způsoby (reduce atd.), ovšem při použití smyčky loop lze k tomuto účelu využít klauzuli sum pro akumulaci výsledků:

(defvar lst '(1 2 3 4 5 6 7 8 9 10))
 
(defvar result
    (loop for i in lst
          sum i))
 
(format T "Result: ~d~%" result)

Předchozí zápis vracel implicitně jedinou hodnotu ze smyčky, a to konkrétně sumu prvků. Toto chování lze popsat i explicitně s využitím klauzule finally, do níž zapíšeme příkaz, který se má vykonat při ukončování smyčky. Povšimněte si, že zde používáme lokální proměnnou total (lze ji pojmenovat různě):

(terpri)
(setq result
    (loop for i in lst
          sum i into total
          finally (return total)))
 
(format T "Result: ~d~%" result)

Výsledkem bude v obou případech stejná zpráva:

Result: 55
Poznámka: právě na těchto příkladech asi začíná být viditelná síla doménově specifického jazyka makra loop.

Předchozí demonstrační příklad je možné ještě více „vyšperkovat“, například vypočítat počet všech prvků, jejich součet, maximální hodnotu a minimální hodnotu. To vše v jediné smyčce a bez použití podmínek. Povšimněte si způsobu, jak ze smyčky vrátit více hodnot:

(defvar lst '(1 2 3 4 5 6 7 8 9 10))
 
(setq result
    (loop for i in lst
             count i into counter
             sum i into total
             maximize i into max-value
             minimize i into min-value
             finally return (list min-value max-value total counter)))
 
(format T "Min value ~d~%" (nth 0 result))
(format T "Max value ~d~%" (nth 1 result))
(format T "Sum value ~d~%" (nth 2 result))
(format T "Values ~d~%" (nth 3 result))

Výsledky vypočtené předchozím příkladem:

Min value 0
Max value 10
Sum value 55
Values 11
Poznámka: namísto (list hodnoty) lze použít i (values hodnoty), což je specialita Common Lispu, o níž se zmíníme příště.

16. Překlad LISPovského programu do nativního kódu

Velmi důležitým konceptem, který v Common Lisp nalezneme, je překlad celého prostředí Common Lispu, pochopitelně včetně uživatelem vytvořeného projektu, do nativního kódu. Výsledkem překladu je skutečně celé lispovské prostředí, a to včetně debuggeru atd. To má několik důsledků, například možnost relativně snadno ladit službu běžící v (před)produkčním prostředí atd., výkonnost například webového serveru naprogramovaného v Lispu je obecně mnohonásobně vyšší, než v případě Ruby či Pythonu atd.

Podívejme se nyní na způsob vytvoření spustitelného (a v rámci dané architektury a operačního systému i přenositelného) programu, po jehož spuštění se vypočítá faktoriál. Nejprve spustíme prostředí SBCL:

$ sbcl
 
This is SBCL 2.0.1.debian, an implementation of ANSI Common Lisp.
More information about SBCL is available at <http://www.sbcl.org/>.
 
SBCL is free software, provided as is, with absolutely no warranty.
It is mostly in the public domain; some portions are provided under
BSD-style licenses.  See the CREDITS and COPYING files in the
distribution for more information.

Dále načteme zdrojový kód uložený v souboru factorial2.lisp:

* (load "factorial2.lisp")
T

A provedeme překlad zavoláním funkce sb-ext:save-lisp-and-die, které se předá jméno výsledného spustitelného kódu, příznak, že se má skutečně vygenerovat spustitelný kód a v neposlední řadě se uvede i jméno funkce, která se má spustit (na rozdíl od Clojure tedy nemusíme tuto funkci pojmenovat main-):

* (sb-ext:save-lisp-and-die "factorial" :executable t :toplevel 'print-factorials)

Průběh překladu (je prakticky okamžitý):

[undoing binding stack and other enclosing state... done]
[performing final GC... done]
[defragmenting immobile space... (fin,inst,fdefn,code,sym)=348+687+14943+16321+24770... done]
[saving current Lisp image into factorial:
writing 0 bytes from the read-only space at 0x50000000
writing 384 bytes from the static space at 0x50100000
writing 22183936 bytes from the dynamic space at 0x1000000000
writing 1798144 bytes from the immobile space at 0x50300000
writing 10579968 bytes from the immobile space at 0x52100000
done]

Výsledkem překladu je dosti objemný soubor o velikosti 34MB!:

$ ls -lah factorial
-rwxr-xr-x. 1 tisnik tisnik 34M Apr 10 15:01 factorial

Který je ovšem plně funkční:

$ ./factorial
 
1
2
6
24
120
720
5040
40320
362880
3628800
39916800
479001600
6227020800
87178291200
1307674368000
20922789888000
355687428096000
6402373705728000
121645100408832000
2432902008176640000

Závislosti na dalších systémových knihovnách jsou v tomto případě skutečně malé:

$ ldd factorial
 
        linux-vdso.so.1 (0x00007fff3d663000)
        libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f78929bc000)
        libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f7892999000)
        libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007f789297d000)
        libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f789282e000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f789263c000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f78929d9000)
Poznámka: existují způsoby, jak velikost tohoto souboru zmenšit; některé z nich si ukážeme příště.

Na tomto místě je vhodné poznamenat, že například do určité míry „konkurenční“ Clojure je založeno na premise „virtuální stroje jsou budoucí operační systémy“ (což se dnes ukazuje být spíše nepravdivé), takže se namísto přenositelného binárního souboru vytváří Java archivy. Z tohoto pohledu je přístup Common Lispu až překvapivě moderní.

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

Zdrojové kódy všech dnes použitých demonstračních příkladů určených pro spuštění v prostředí Common Lispu 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:

# Příklad Popis příkladu Cesta
1 boolean_ops.lisp pravdivostní operace https://github.com/tisnik/lisp-families/blob/master/common-lisp/boolean_ops.lisp
2 cond.lisp základní rozhodovací konstrukce https://github.com/tisnik/lisp-families/blob/master/common-lisp/cond.lisp
3 cons.lisp konstrukce seznamů https://github.com/tisnik/lisp-families/blob/master/common-lisp/cons.lisp
4 dotimes1.lisp použití makra dotimes https://github.com/tisnik/lisp-families/blob/master/common-lisp/dotimes1.lisp
5 dotimes2.lisp použití makra dotimes https://github.com/tisnik/lisp-families/blob/master/common-lisp/dotimes2.lisp
6 dotimes3.lisp použití makra dotimes https://github.com/tisnik/lisp-families/blob/master/common-lisp/dotimes3.lisp
7 dot_pairs.lisp datový typ „pár“ https://github.com/tisnik/lisp-families/blob/master/common-lisp/dot_pairs.lisp
8 drop.lisp funkce drop pro zpracování seznamů https://github.com/tisnik/lisp-families/blob/master/common-lisp/drop.lisp
9 factorial1.lisp výpočet faktoriálu, první varianta https://github.com/tisnik/lisp-families/blob/master/common-lisp/factorial1.lisp
10 factorial2.lisp výpočet faktoriálu, druhá varianta https://github.com/tisnik/lisp-families/blob/master/common-lisp/factorial2.lisp
11 function1.lisp funkce v Common Lispu https://github.com/tisnik/lisp-families/blob/master/common-lisp/function1.lisp
12 function2.lisp funkce v Common Lispu https://github.com/tisnik/lisp-families/blob/master/common-lisp/function2.lisp
13 function3.lisp funkce v Common Lispu https://github.com/tisnik/lisp-families/blob/master/common-lisp/function3.lisp
14 function4.lisp funkce v Common Lispu https://github.com/tisnik/lisp-families/blob/master/common-lisp/function4.lisp
15 hello1.lisp „Hello world“ v Common Lispu https://github.com/tisnik/lisp-families/blob/master/common-lisp/hello1.lisp
16 hello2.lisp „Hello world“ v Common Lispu https://github.com/tisnik/lisp-families/blob/master/common-lisp/hello2.lisp
17 lists.lisp operace se seznamy https://github.com/tisnik/lisp-families/blob/master/common-lisp/lists.lisp
18 predicates.lisp vybrané predikáty https://github.com/tisnik/lisp-families/blob/master/common-lisp/predicates.lisp
19 take.lisp funkce take pro zpracování seznamů https://github.com/tisnik/lisp-families/blob/master/common-lisp/take.lisp
       
20 loop_append.lisp klauzule append https://github.com/tisnik/lisp-families/blob/master/common-lisp/loop_append.lisp
21 loop_for_from_dowto.lisp smyčka s počitadlem https://github.com/tisnik/lisp-families/blob/master/common-lisp/loop_for_from_dowto.lisp
22 loop_for_from_to_by.lisp smyčka s počitadlem https://github.com/tisnik/lisp-families/blob/master/common-lisp/loop_for_from_to_by.lisp
23 loop_for_from_to.lisp smyčka s počitadlem https://github.com/tisnik/lisp-families/blob/master/common-lisp/loop_for_from_to.lisp
24 loop_for_in_by.lisp procházení seznamu s klauzulí in a by https://github.com/tisnik/lisp-families/blob/master/common-lisp/loop_for_in_by.lisp
25 loop_for_in.lisp procházení seznamu s klauzulí in https://github.com/tisnik/lisp-families/blob/master/common-lisp/loop_for_in.lisp
26 loop_for_on_by.lisp procházení seznamem s klauzulí on https://github.com/tisnik/lisp-families/blob/master/common-lisp/loop_for_on_by.lisp
27 loop_for_on.lisp procházení seznamem s klauzulí on https://github.com/tisnik/lisp-families/blob/master/common-lisp/loop_for_on.lisp
28 loop_for_to.lisp smyčka s počitadlem https://github.com/tisnik/lisp-families/blob/master/common-lisp/loop_for_to.lisp
29 loop_maximize.lisp vyhledání prvku s největším výsledkem výpočtu https://github.com/tisnik/lisp-families/blob/master/common-lisp/loop_maximize.lisp
30 loop_max_min_count_sum.lisp statistické informace o seznamu https://github.com/tisnik/lisp-families/blob/master/common-lisp/loop_max_min_count_sum.lisp
31 loop_repeat.lisp klauzule repeat https://github.com/tisnik/lisp-families/blob/master/common-lisp/loop_repeat.lisp
32 loop_sum.lisp výpočet sumy prvků https://github.com/tisnik/lisp-families/blob/master/common-lisp/loop_sum.lisp
33 loop_while_until.lisp klauzule while a until https://github.com/tisnik/lisp-families/blob/master/common-lisp/loop_while_until.lisp
34 broken_factorial.lisp chyba při výpočtu faktoriálu https://github.com/tisnik/lisp-families/blob/master/common-lisp/broken_factorial.lisp
35 broken_factorial2.lisp chyba při výpočtu faktoriálu https://github.com/tisnik/lisp-families/blob/master/common-lisp/broken_factorial2.lisp
Poznámka: všechny tyto demonstrační příklady je možné spustit přímo z shellu: sbcl –load <jméno-souboru.lisp>

18. 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 (kromě samostatného seriálu, který se věnoval programovacímu jazyku Clojure):

bitcoin školení listopad 24

  1. 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/
  2. PicoLisp: minimalistický a přitom překvapivě výkonný interpret Lispu
    https://www.root.cz/clanky/picolisp-minimalisticky-a-pritom-prekvapive-vykonny-interpret-lispu/
  3. 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/
  4. 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/
  5. GNU Guile – interpret Scheme vestavitelný do nativních aplikací
    https://www.root.cz/clanky/gnu-guile-interpret-scheme-vestavitelny-do-nativnich-aplikaci/
  6. 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/
  7. 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/
  8. Jazyk Kawa v ekosystému virtuálního stroje Javy
    https://www.root.cz/clanky/jazyk-kawa-v-ekosystemu-virtualniho-stroje-javy/
  9. 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/
  10. 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/
  11. Makra v Racketu i v dalších lispovských jazycích
    https://www.root.cz/clanky/makra-v-racketu-i-v-dalsich-lispovskych-jazycich/
  12. Základní knihovna jazyka Racket
    https://www.root.cz/clanky/zakladni-knihovna-jazyka-racket/
  13. Jazyk Joker: dialekt Clojure naprogramovaný v Go
    https://www.root.cz/clanky/jazyk-joker-dialekt-clojure-naprogramovany-v-go/
  14. 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/
  15. Projekt Gambit – další kvalitní interpret i překladač programovacího jazyka Scheme
    https://www.root.cz/clanky/projekt-gambit-dalsi-kvalitni-interpret-i-prekladac-programovaciho-jazyka-scheme/
  16. Interlisp aneb oživujeme dinosaura
    https://www.root.cz/clanky/interlisp-aneb-ozivujeme-dinosaura/
  17. Propojení světa LISPu se světem JavaScriptu s využitím transpřekladače Wisp
    https://www.root.cz/clanky/propojeni-sveta-lispu-se-svetem-javascriptu-s-vyuzitim-transprekladace-wisp/
  18. Propojení světa LISPu se světem JavaScriptu s využitím transpřekladače Wisp (2.část)
    https://www.root.cz/clanky/propojeni-sveta-lispu-se-svetem-javascriptu-s-vyuzitim-transprekladace-wisp-2-cast/

Články o Elispu:

  1. Úpravy Emacsu a tvorba nových modulů s využitím Emacs Lispu
    https://www.root.cz/clanky/upravy-emacsu-a-tvorba-novych-modulu-s-vyuzitim-emacs-lispu/
  2. Úpravy Emacsu s Emacs Lisp: základní konstrukce jazyka
    https://www.root.cz/clanky/upravy-emacsu-s-emacs-lisp-zakladni-konstrukce-jazyka/
  3. Úpravy Emacsu s Emacs Lisp: všemocné makro cl-loop a knihovna dash
    https://www.root.cz/clanky/upravy-emacsu-s-emacs-lisp-vsemocne-makro-cl-loop-a-knihovna-dash/
  4. Úpravy Emacsu s Emacs Lisp: možnosti nabízené knihovnou Dash
    https://www.root.cz/clanky/upravy-emacsu-s-emacs-lisp-moznosti-nabizene-knihovnou-dash/
  5. Úpravy Emacsu s Emacs Lisp: dokončení popisu Emacs Lispu
    https://www.root.cz/clanky/upravy-emacsu-s-emacs-lisp-dokonceni-popisu-emacs-lispu/
  6. Úpravy Emacsu s Emacs Lisp: manipulace se základními datovými strukturami Emacsu
    https://www.root.cz/clanky/upravy-emacsu-s-emacs-lisp-manipulace-se-zakladnimi-datovymi-strukturami-emacsu/

19. Literatura

O Common Lispu, Scheme či Clojure, tedy o třech (s velkou pravděpodobností) nejpoužívanějších dialektech LISPu, vyšlo poměrně velké množství literatury. Pro Common Lisp je typická jeho velká stabilita, a to minimálně od roku 1994, což mj. znamená, že i původní vydaní prvních dvou dále zmíněných knih je zcela bez problémů použitelné i dnes (a obě knihy jsou navíc dobře čitelné):

  1. Peter Seibel
    „Practical Common Lisp“
    2009
  2. Paul Graham
    „ANSI Common Lisp“
    1995
  3. Gerald Gazdar
    „Natural Language Processing in Lisp: An Introduction to Computational Linguistics“
    1989
  4. Peter Norvig
    „Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp“
    1991
  5. Alex Mileler et.al.
    „Clojure Applied: From Practice to Practitioner“
    2015
  6. „Living Clojure: An Introduction and Training Plan for Developers“
    2015
  7. Dmitri Sotnikov
    „Web Development with Clojure: Build Bulletproof Web Apps with Less Code“
    2016
  8. McCarthy
    „Recursive functions of symbolic expressions and their computation by machine, part I“
    1960
  9. R. Kent Dybvig
    „The Scheme Programming Language“
    2009
  10. Max Hailperin
    „Concrete Abstractions“
    1998
  11. Guy L. Steele
    „History of Scheme“
    2006, Sun Microsystems Laboratories
  12. Kolář J., Muller K.:
    „Speciální programovací jazyky“
    Praha 1981
  13. „AutoLISP Release 9, Programmer's reference“
    Autodesk Ltd., October 1987
  14. „AutoLISP Release 10, Programmer's reference“
    Autodesk Ltd., September 1988
  15. 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
  16. Carl Hewitt; Peter Bishop and Richard Steiger
    „A Universal Modular Actor Formalism for Artificial Intelligence“
    1973
  17. Feiman, J.
    „The Gartner Programming Language Survey (October 2001)“
    Gartner Advisory
  18. 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)
  19. Paul Graham
    On Lisp
    Prentice Hall, 1993
    Dostupné online na adrese http://www.paulgraham.com/on­lisptext.html
  20. David S. Touretzky
    Common LISP: A Gentle Introduction to Symbolic Computation (Dover Books on Engineering)
  21. Peter Norvig
    Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp
  22. Patrick Winston, Berthold Horn
    Lisp (3rd Edition)
    ISBN-13: 978–0201083194, ISBN-10: 0201083191
  23. 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. Odkazy na Internetu

  1. Common Lisp
    https://lisp-lang.org/
  2. Why You Should Learn Lisp In 2022?
    https://www.youtube.com/wat­ch?v=GWdf1flcLoM
  3. LOOP Common Lisps Superior For
    https://www.youtube.com/wat­ch?v=i4tmF_1nZng
  4. Lisp VS C benchmarks
    https://programming-language-benchmarks.vercel.app/lisp-vs-c
  5. Common Lisp: An elegant design pattern
    https://www.youtube.com/wat­ch?v=9597LFlvMuE
  6. Common Lisp Macros By Example Tutorial
    https://lisp-journey.gitlab.io/blog/common-lisp-macros-by-example-tutorial/
  7. The Common Lisp Cookbook
    https://lispcookbook.github.io/cl-cookbook/
  8. The Evolution of Lisp
    https://www.csee.umbc.edu/cou­rses/331/resources/papers/E­volution-of-Lisp.pdf
  9. Awesome CL
    https://github.com/CodyRe­ichert/awesome-cl
  10. LISP
    https://taoofmac.com/space/dev/lisp
  11. Repositář projektu femtolisp
    https://github.com/JeffBe­zanson/femtolisp
  12. Femtolisp – lightweight, robust lisp interpreter built on reusable C libraries
    https://www.findbestopensou­rce.com/product/femtolisp
  13. YCombinator: Femtolisp: A lightweight, robust, scheme-like Lisp implementation
    https://news.ycombinator.com/i­tem?id=22094722
  14. Learning Julia by Anshul Joshi, Rahul Lakhanpal: Femtolisp
    https://www.oreilly.com/li­brary/view/learning-julia/9781785883279/2e85442f-d100–4b53-b8f7–7d20d62f0255.xhtml
  15. The role of femtolisp in Julia?
    https://discourse.julialang.org/t/the-role-of-femtolisp-in-julia/1902
  16. LispSyntax.jl: A clojure-like lisp syntax for julia
    https://github.com/swadey/Lis­pSyntax.jl
  17. What exactly code lowering is an how to do “unlowering”?
    https://discourse.julialang.org/t/what-exactly-code-lowering-is-an-how-to-do-unlowering/1315
  18. Interlisp.org: Dedicated to Restoring and Preserving the Interlisp experience
    https://github.com/Interlisp
  19. Warren Teitelman
    https://en.wikipedia.org/wi­ki/Warren_Teitelman
  20. InterLISP/65
    http://www.atarimania.com/utility-atari-400–800-xl-xe-interlisp-65_12477.html
  21. Lisp Editing in the 80s – Interlisp SEdit (Video)
    https://www.youtube.com/wat­ch?v=2qsmF8HHskg
  22. Inter-LISP
    http://www.atarimania.com/utility-atari-400–800-xl-xe-inter-lisp_29354.html
  23. InterLISP 65 Editing (video)
    https://www.youtube.com/wat­ch?v=nY_hcazo86A
  24. Datasoft INTER-LISP/65 (Atari Age, chat)
    https://atariage.com/forum­s/topic/116093-datasoft-inter-lisp65/
  25. Marvin Minsky – The beauty of the Lisp language (44/151)
    https://www.youtube.com/wat­ch?v=YaWVHyIBVeI
  26. History of LISP (Interlisp)
    http://www.softwarepreser­vation.org/projects/LISP/in­dex.html#INTERLISP_
  27. Computer-Assisted Instruction (Bits and Bytes, Episode 7)
    https://www.youtube.com/wat­ch?v=eURtTV_qKw8
  28. Můžeme věřit překladačům? Projekty řešící schéma „důvěřivé důvěry“
    https://www.root.cz/clanky/muzeme-verit-prekladacum-projekty-resici-schema-duverive-duvery/
  29. Gambit in the browser
    https://feeley.github.io/gambit-in-the-browser/
  30. A Tour of Scheme in Gambit
    http://dynamo.iro.umontre­al.ca/wiki/images/a/a7/A_Tou­r_of_Scheme_in_Gambit.pdf
  31. Gambit Scheme: Inside Out
    http://www.iro.umontreal.ca/~gam­bit/Gambit-inside-out.pdf
  32. Gambit Internal Documentation
    http://dynamo.iro.umontre­al.ca/wiki/index.php/Inter­nal_Documentation
  33. clojure-scheme: Compiling to Native Code via Scheme
    http://www.iro.umontreal.ca/~gam­bit/Sorenson-Clojure-to-Native-via-Scheme.pdf
  34. Gauche – a Scheme implementation
    http://practical-scheme.net/gauche/
  35. Scheme48
    https://s48.org/
  36. SISC (Second Interpreter of Scheme)
    http://sisc-scheme.org/
  37. The SCM Implementation of Scheme
    https://people.csail.mit.e­du/jaffer/SCM.html
  38. Ypsilon – The ultimate script language system for the video pinball fourth generation
    http://www.littlewingpinba­ll.com/doc/en/ypsilon/index­.html
  39. Chicken Scheme
    https://call-cc.org/
  40. Eggs Unlimited
    http://wiki.call-cc.org/chicken-projects/egg-index-5.html
  41. Chicken Scheme Wiki
    https://wiki.call-cc.org/
  42. CHICKEN for Python programmers
    https://wiki.call-cc.org/chicken-for-python-programmers
  43. Programming for Performance
    http://wiki.call-cc.org/programming-for-performance
  44. Using the compiler
    https://wiki.call-cc.org/man/4/Using%20the%20compiler
  45. CHICKEN Scheme tutorials
    https://wiki.call-cc.org/tutorials
  46. Traditional Turtles
    https://docs.racket-lang.org/turtles/Traditio­nal_Turtles.html
  47. [racket] How best to repeat a function call n times?
    https://lists.racket-lang.org/users/archive/2014-September/064203.html
  48. Racket: Macros
    https://www.it.uu.se/edu/cou­rse/homepage/avfunpro/ht13/lec­tures/Racket-3-Macros.pdf
  49. Beautiful Racket / explainers: Macros
    https://beautifulracket.com/ex­plainer/macros.html
  50. Macros (dokumentace k Racketu)
    https://docs.racket-lang.org/guide/macros.html
  51. Model syntaxe jazyka Racket
    https://docs.racket-lang.org/reference/syntax-model.html
  52. Syntax Objects
    https://docs.racket-lang.org/guide/stx-obj.html
  53. Tech behind Tech: Clojure Macros Simplified
    http://techbehindtech.com/2010/09/28/clo­jure-macros-simplified/
  54. Fatvat – Exploring functional programming: Clojure Macros
    http://www.fatvat.co.uk/2009/02/clo­jure-macros.html
  55. Beautiful Racket: an introduction to language-oriented programming using Racket
    https://beautifulracket.com/
  56. Stránky projektu Racket
    https://racket-lang.org/
  57. Dokumentace k projektu Racket
    https://docs.racket-lang.org/index.html
  58. Seznam dostupných balíčků pro Racket
    https://pkgs.racket-lang.org/
  59. Racket na Wikipedii
    https://en.wikipedia.org/wi­ki/Racket_(programming_lan­guage)
  60. Vector Library (R7RS-compatible)
    https://srfi.schemers.org/srfi-133/srfi-133.html
  61. Blogy o Racketu a navazujících technologiích
    https://blog.racket-lang.org/
  62. Prográmky psané v Racketu na RosettaCode
    http://rosettacode.org/wi­ki/Category:Racket
  63. Fear of Macros
    https://www.greghendershott.com/fear-of-macros/
  64. Rackjure
    https://github.com/greghen­dershott/rackjure
  65. 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/
  66. Racket News
    https://racket-news.com/
  67. Racket: Lisp for learning
    https://lwn.net/Articles/795385/
  68. Future of Racket
    https://www.greghendershot­t.com/2019/07/future-of-racket.html
  69. Vectors (pro Gauche)
    https://practical-scheme.net/gauche/man/gauche-refe/Vectors.html
  70. Kawa: Compiling Scheme to Java
    https://www.mit.edu/afs.new/sip­b/project/kawa/doc/kawa-tour.html
  71. Kawa in Languages shootout
    http://per.bothner.com/blog/2010/Kawa-in-shootout/
  72. Kawa 2.0 Supports Scheme R7RS
    https://developers.slashdot­.org/story/14/12/13/2259225/ka­wa-20-supports-scheme-r7rs/
  73. Kawa — fast scripting on the Java platform
    https://lwn.net/Articles/623349/
  74. Tail call (a její optimalizace)
    https://en.wikipedia.org/wi­ki/Tail_call
  75. SLIME (Wikipedia)
    http://en.wikipedia.org/wiki/SLIME
  76. slime.vim
    http://s3.amazonaws.com/mps/slime.vim
  77. What are the best scheme implementations?
    https://www.slant.co/topic­s/5282/~scheme-implementations
  78. Bigloo homepage
    http://www-sop.inria.fr/mimosa/fp/Bigloo/
  79. FTP s tarbally Bigloo
    ftp://ftp-sop.inria.fr/indes/fp/Bigloo
  80. GOTO 2018 • Functional Programming in 40 Minutes • Russ Olsen
    https://www.youtube.com/wat­ch?v=0if71HOyVjY
  81. TinyScheme (stránka na Sourceforge)
    http://tinyscheme.sourcefor­ge.net/home.html
  82. Embedding Tiny Scheme in a Game
    http://www.silicondelight­.com/embedding-tiny-scheme-in-a-game/
  83. Embedding Scheme for a game mission scripting DSL
    http://carloscarrasco.com/embedding-scheme-for-a-game-mission-scripting-dsl.html
  84. Všechny verze TinyScheme na SourceForge
    https://sourceforge.net/pro­jects/tinyscheme/files/ti­nyscheme/
  85. Fork TinyScheme na GitHubu
    https://github.com/yawnt/tinyscheme
  86. Ackermannova funkce
    https://cs.wikipedia.org/wi­ki/Ackermannova_funkce
  87. Ackermann function na Rosetta Code
    https://rosettacode.org/wi­ki/Ackermann_function#Sche­me
  88. Success Stories (lisp.org)
    https://lisp-lang.org/success/
  89. Allegro Common Lisp Success Stories
    https://franz.com/success/
  90. Clojure Success Stories
    https://clojure.org/commu­nity/success_stories
  91. Scheme Quick Reference
    https://www.st.cs.uni-saarland.de/edu/config-ss04/scheme-quickref.pdf
  92. Slajdy o Scheme (od slajdu číslo 15)
    https://docs.google.com/pre­sentation/d/1abmDnKjrq1tcjGvvRNAK­hOiSTSE2lyagtcEPal07Gbo/e­dit
  93. Scheme Cheat Sheet
    https://github.com/smythp/scheme-cheat-sheet
  94. Embedding Lua, embedding Guile
    http://puntoblogspot.blog­spot.com/2013/04/embedding-lua-embedding-guile.html
  95. Lambda Papers
    https://en.wikisource.org/wi­ki/Lambda_Papers
  96. Revised7Report on the Algorithmic Language Scheme
    https://small.r7rs.org/at­tachment/r7rs.pdf
  97. 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/
  98. Why is Scheme my first language in university?
    https://softwareengineerin­g.stackexchange.com/questi­ons/115252/why-is-scheme-my-first-language-in-university
  99. The Perils of JavaSchools
    https://www.joelonsoftware­.com/2005/12/29/the-perils-of-javaschools-2/
  100. How to Design Programs, Second Edition
    https://htdp.org/2019–02–24/index.html
  101. LilyPond
    http://lilypond.org/
  102. LilyPond — Extending (přes Scheme)
    http://lilypond.org/doc/v2­.18/Documentation/extendin­g/scheme-tutorial
  103. Scheme in LilyPond
    http://lilypond.org/doc/v2­.18/Documentation/extendin­g/scheme-in-lilypond
  104. GnuCash
    http://www.gnucash.org/
  105. Custom Reports (in GNU Cash)
    https://wiki.gnucash.org/wi­ki/Custom_Reports
  106. Program by Design
    https://programbydesign.org/
  107. SchemePy
    https://pypi.org/project/SchemePy/
  108. 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
  109. femtolisp
    https://github.com/JeffBe­zanson/femtolisp
  110. (How to Write a (Lisp) Interpreter (in Python))
    http://norvig.com/lispy.html
  111. Repositář s Guile Emacsem
    http://git.hcoop.net/?p=bpt/guile.git
  112. Interacting with Guile Compound Data Types in C
    http://www.lonelycactus.com/gu­ilebook/x1555.html
  113. Calling Guile functions from C
    http://www.lonelycactus.com/gu­ilebook/c1204.html#SECCAL­LGUILEFUNC
  114. Arrays, and other compound data types
    http://www.lonelycactus.com/gu­ilebook/charrays.html
  115. Interacting with Guile Compound Data Types in C
    http://www.lonelycactus.com/gu­ilebook/x1555.html
  116. Guile Reference Manual
    https://www.gnu.org/softwa­re/guile/manual/html_node/in­dex.html
  117. Scheme: Summary of Common Syntax
    https://www.gnu.org/softwa­re/guile/manual/html_node/Syn­tax-Summary.html#Syntax-Summary
  118. Scripting with Guile: Extension language enhances C and Scheme
    https://www.ibm.com/develo­perworks/library/l-guile/index.html
  119. Having fun with Guile: a tutorial
    http://dustycloud.org/misc/guile-tutorial.html
  120. Guile: Loading Readline Support
    https://www.gnu.org/softwa­re/guile/manual/html_node/Lo­ading-Readline-Support.html#Loading-Readline-Support
  121. lispy
    https://pypi.org/project/lispy/
  122. Lython
    https://pypi.org/project/Lython/
  123. Lizpop
    https://pypi.org/project/lizpop/
  124. Budoucnost programovacích jazyků
    http://www.knesl.com/budoucnost-programovacich-jazyku
  125. LISP Prolog and Evolution
    http://blog.samibadawi.com/2013/05/lisp-prolog-and-evolution.html
  126. List of Lisp-family programming languages
    https://en.wikipedia.org/wi­ki/List_of_Lisp-family_programming_languages
  127. clojure_py na indexu PyPi
    https://pypi.python.org/py­pi/clojure_py
  128. PyClojure
    https://github.com/eigenhom­bre/PyClojure
  129. Hy na GitHubu
    https://github.com/hylang/hy
  130. Hy: The survival guide
    https://notes.pault.ag/hy-survival-guide/
  131. Hy běžící na monitoru terminálu společnosti Symbolics
    http://try-hy.appspot.com/
  132. Welcome to Hy’s documentation!
    http://docs.hylang.org/en/stable/
  133. Hy na PyPi
    https://pypi.org/project/hy/#des­cription
  134. Getting Hy on Python
    https://lwn.net/Articles/596626/
  135. Programming Can Be Fun with Hy
    https://opensourceforu.com/2014/02/pro­gramming-can-fun-hy/
  136. Přednáška o projektu Hy (pětiminutový lighttalk)
    http://blog.pault.ag/day/2013/04/02
  137. Hy (Wikipedia)
    https://en.wikipedia.org/wiki/Hy
  138. GNU Emacs Lisp Reference Manual: Point
    https://www.gnu.org/softwa­re/emacs/manual/html_node/e­lisp/Point.html
  139. GNU Emacs Lisp Reference Manual: Narrowing
    https://www.gnu.org/softwa­re/emacs/manual/html_node/e­lisp/Narrowing.html
  140. GNU Emacs Lisp Reference Manual: Functions that Create Markers
    https://www.gnu.org/softwa­re/emacs/manual/html_node/e­lisp/Creating-Markers.html
  141. GNU Emacs Lisp Reference Manual: Motion
    https://www.gnu.org/softwa­re/emacs/manual/html_node/e­lisp/Motion.html#Motion
  142. GNU Emacs Lisp Reference Manual: Basic Char Syntax
    https://www.gnu.org/softwa­re/emacs/manual/html_node/e­lisp/Basic-Char-Syntax.html
  143. Elisp: Sequence: List, Array
    http://ergoemacs.org/emac­s/elisp_list_vs_vector.html
  144. Elisp: Property List
    http://ergoemacs.org/emac­s/elisp_property_list.html
  145. Elisp: Hash Table
    http://ergoemacs.org/emac­s/elisp_hash_table.html
  146. Elisp: Association List
    http://ergoemacs.org/emac­s/elisp_association_list.html
  147. The mapcar Function (An Introduction to Programming in Emacs Lisp)
    https://www.gnu.org/softwa­re/emacs/manual/html_node/e­intr/mapcar.html
  148. Anaphoric macro
    https://en.wikipedia.org/wi­ki/Anaphoric_macro
  149. Some Common Lisp Loop Macro Examples
    https://www.youtube.com/wat­ch?v=3yl8o6r_omw
  150. A Guided Tour of Emacs
    https://www.gnu.org/softwa­re/emacs/tour/
  151. The Roots of Lisp
    http://www.paulgraham.com/ro­otsoflisp.html
  152. Evil (Emacs Wiki)
    https://www.emacswiki.org/emacs/Evil
  153. Evil (na GitHubu)
    https://github.com/emacs-evil/evil
  154. Evil (na stránkách repositáře MELPA)
    https://melpa.org/#/evil
  155. Evil Mode: How I Switched From VIM to Emacs
    https://blog.jakuba.net/2014/06/23/e­vil-mode-how-to-switch-from-vim-to-emacs.html
  156. GNU Emacs (home page)
    https://www.gnu.org/software/emacs/
  157. GNU Emacs (texteditors.org)
    http://texteditors.org/cgi-bin/wiki.pl?GnuEmacs
  158. An Introduction To Using GDB Under Emacs
    http://tedlab.mit.edu/~dr/gdbin­tro.html
  159. An Introduction to Programming in Emacs Lisp
    https://www.gnu.org/softwa­re/emacs/manual/html_node/e­intr/index.html
  160. 27.6 Running Debuggers Under Emacs
    https://www.gnu.org/softwa­re/emacs/manual/html_node/e­macs/Debuggers.html
  161. GdbMode
    http://www.emacswiki.org/e­macs/GdbMode
  162. Emacs (Wikipedia)
    https://en.wikipedia.org/wiki/Emacs
  163. Emacs timeline
    http://www.jwz.org/doc/emacs-timeline.html
  164. Emacs Text Editors Family
    http://texteditors.org/cgi-bin/wiki.pl?EmacsFamily
  165. Vrapper aneb spojení možností Vimu a Eclipse
    https://mojefedora.cz/vrapper-aneb-spojeni-moznosti-vimu-a-eclipse/
  166. 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/
  167. Emacs/Evil-mode – A basic reference to using evil mode in Emacs
    http://www.aakarshnair.com/posts/emacs-evil-mode-cheatsheet
  168. From Vim to Emacs+Evil chaotic migration guide
    https://juanjoalvarez.net/es/de­tail/2014/sep/19/vim-emacsevil-chaotic-migration-guide/
  169. Introduction to evil-mode {video)
    https://www.youtube.com/wat­ch?v=PeVQwYUxYEg
  170. EINE (Emacs Wiki)
    http://www.emacswiki.org/emacs/EINE
  171. EINE (Texteditors.org)
    http://texteditors.org/cgi-bin/wiki.pl?EINE
  172. ZWEI (Emacs Wiki)
    http://www.emacswiki.org/emacs/ZWEI
  173. ZWEI (Texteditors.org)
    http://texteditors.org/cgi-bin/wiki.pl?ZWEI
  174. Zmacs (Wikipedia)
    https://en.wikipedia.org/wiki/Zmacs
  175. Zmacs (Texteditors.org)
    http://texteditors.org/cgi-bin/wiki.pl?Zmacs
  176. TecoEmacs (Emacs Wiki)
    http://www.emacswiki.org/e­macs/TecoEmacs
  177. Micro Emacs
    http://www.emacswiki.org/e­macs/MicroEmacs
  178. Micro Emacs (Wikipedia)
    https://en.wikipedia.org/wi­ki/MicroEMACS
  179. EmacsHistory
    http://www.emacswiki.org/e­macs/EmacsHistory
  180. Seznam editorů s ovládáním podobným Emacsu či kompatibilních s příkazy Emacsu
    http://www.finseth.com/emacs.html
  181. evil-numbers
    https://github.com/cofi/evil-numbers
  182. Debuggery a jejich nadstavby v Linuxu (1.část)
    http://fedora.cz/debuggery-a-jejich-nadstavby-v-linuxu/
  183. Debuggery a jejich nadstavby v Linuxu (2.část)
    http://fedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-2-cast/
  184. Debuggery a jejich nadstavby v Linuxu (3): Nemiver
    http://fedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-3-nemiver/
  185. Debuggery a jejich nadstavby v Linuxu (4): KDbg
    http://fedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-4-kdbg/
  186. 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/
  187. Org mode
    https://orgmode.org/
  188. The Org Manual
    https://orgmode.org/manual/index.html
  189. Kakoune (modální textový editor)
    http://kakoune.org/
  190. Vim-style keybinding in Emacs/Evil-mode
    https://gist.github.com/tro­yp/6b4c9e1c8670200c04c16036805773d8
  191. Emacs – jak začít
    http://www.abclinuxu.cz/clan­ky/navody/emacs-jak-zacit
  192. Programovací jazyk LISP a LISP machines
    https://www.root.cz/clanky/pro­gramovaci-jazyk-lisp-a-lisp-machines/
  193. Evil-surround
    https://github.com/emacs-evil/evil-surround
  194. Spacemacs
    http://spacemacs.org/
  195. Lisp: Common Lisp, Racket, Clojure, Emacs Lisp
    http://hyperpolyglot.org/lisp
  196. Common Lisp, Scheme, Clojure, And Elisp Compared
    http://irreal.org/blog/?p=725
  197. Does Elisp Suck?
    http://irreal.org/blog/?p=675
  198. Emacs pro mírně pokročilé (9): Elisp
    https://www.root.cz/clanky/emacs-elisp/
  199. If I want to learn lisp, are emacs and elisp a good choice?
    https://www.reddit.com/r/e­macs/comments/2m141y/if_i_wan­t_to_learn_lisp_are_emacs_an­d_elisp_a/
  200. Clojure(Script) Interactive Development Environment that Rocks!
    https://github.com/clojure-emacs/cider
  201. An Introduction to Emacs Lisp
    https://harryrschwartz.com/2014/04/08/an-introduction-to-emacs-lisp.html
  202. Emergency Elisp
    http://steve-yegge.blogspot.com/2008/01/emergency-elisp.html
  203. Lambda calculus
    https://en.wikipedia.org/wi­ki/Lambda_calculus
  204. John McCarthy's original LISP paper from 1959
    https://www.reddit.com/r/pro­gramming/comments/17lpz4/joh­n_mccarthys_original_lisp_pa­per_from_1959/
  205. Micro Manual LISP
    https://www.scribd.com/do­cument/54050141/Micro-Manual-LISP
  206. How Lisp Became God's Own Programming Language
    https://twobithistory.org/2018/10/14/lis­p.html
  207. History of Lisp
    http://jmc.stanford.edu/ar­ticles/lisp/lisp.pdf
  208. The Roots of Lisp
    http://languagelog.ldc.upen­n.edu/myl/llog/jmc.pdf
  209. Racket
    https://racket-lang.org/
  210. The Racket Manifesto
    http://felleisen.org/matthi­as/manifesto/
  211. MIT replaces Scheme with Python
    https://www.johndcook.com/blog/2009/03/26/mit-replaces-scheme-with-python/
  212. Adventures in Advanced Symbolic Programming
    http://groups.csail.mit.e­du/mac/users/gjs/6.945/
  213. Why MIT Switched from Scheme to Python (2009)
    https://news.ycombinator.com/i­tem?id=14167453
  214. Starodávná stránka XLispu
    http://www.xlisp.org/
  215. AutoLISP
    https://en.wikipedia.org/wi­ki/AutoLISP
  216. Seriál PicoLisp: minimalistický a výkonný interpret Lispu
    https://www.root.cz/serialy/picolisp-minimalisticky-a-vykonny-interpret-lispu/
  217. Common Lisp
    https://common-lisp.net/
  218. Getting Going with Common Lisp
    https://cliki.net/Getting%20Started
  219. Online Tutorial (Common Lisp)
    https://cliki.net/online%20tutorial
  220. Guile Emacs
    https://www.emacswiki.org/e­macs/GuileEmacs
  221. Guile Emacs History
    https://www.emacswiki.org/e­macs/GuileEmacsHistory
  222. Guile is a programming language
    https://www.gnu.org/software/guile/
  223. MIT Scheme
    http://groups.csail.mit.e­du/mac/projects/scheme/
  224. SIOD: Scheme in One Defun
    http://people.delphiforum­s.com/gjc//siod.html
  225. CommonLispForEmacs
    https://www.emacswiki.org/e­macs/CommonLispForEmacs
  226. Elisp: print, princ, prin1, format, message
    http://ergoemacs.org/emac­s/elisp_printing.html
  227. Special Forms in Lisp
    http://www.nhplace.com/ken­t/Papers/Special-Forms.html
  228. Basic Building Blocks in LISP
    https://www.tutorialspoin­t.com/lisp/lisp_basic_syn­tax.htm
  229. Introduction to LISP – University of Pittsburgh
    https://people.cs.pitt.edu/~mi­los/courses/cs2740/Lectures/Lis­pTutorial.pdf
  230. Why don't people use LISP
    https://forums.freebsd.org/threads/why-dont-people-use-lisp.24572/
  231. Structured program theorem
    https://en.wikipedia.org/wi­ki/Structured_program_the­orem
  232. Clojure: API Documentation
    https://clojure.org/api/api
  233. Tutorial for the Common Lisp Loop Macro
    http://www.ai.sri.com/pkarp/loop.html
  234. Common Lisp's Loop Macro Examples for Beginners
    http://www.unixuser.org/~e­uske/doc/cl/loop.html
  235. A modern list api for Emacs. No 'cl required.
    https://github.com/magnars/dash.el
  236. The LOOP Facility
    http://www.lispworks.com/do­cumentation/HyperSpec/Body/06_a­.htm
  237. Clojure.org: Vars and the Global Environment
    http://clojure.org/Vars
  238. Clojure.org: Refs and Transactions
    http://clojure.org/Refs
  239. Clojure.org: Atoms
    http://clojure.org/Atoms
  240. Clojure.org: Agents as Asynchronous Actions
    http://clojure.org/agents
  241. Transient Data Structureshttp://clojure.or­g/transients
  242. Dynamic Languages Strike Back
    http://steve-yegge.blogspot.cz/2008/05/dynamic-languages-strike-back.html
  243. Scripting: Higher Level Programming for the 21st Century
    http://www.tcl.tk/doc/scripting.html
  244. Clojure (na Wikipedia EN)
    http://en.wikipedia.org/wiki/Clojure
  245. Clojure (na Wikipedia CS)
    http://cs.wikipedia.org/wiki/Clojure
  246. LISP: Lex Fridman's favorite programming language
    https://www.youtube.com/wat­ch?v=cMMiaCtOzV0
  247. What is the Curse of Lisp?
    https://www.youtube.com/wat­ch?v=_J3×5yvQ8yc
  248. Array Programming Re-Imagined in Lisp
    https://github.com/phantomics/april
  249. What is Nil Punning?
    https://www.youtube.com/wat­ch?v=xiYKuDk6G-o
  250. Python VS Common Lisp, workflow and ecosystem
    https://lisp-journey.gitlab.io/pythonvslisp/
  251. A fast-moving Common Lisp software distribution
    https://ultralisp.org/
  252. Numcl
    https://github.com/numcl/numcl
  253. Petalisp
    https://github.com/marcohe­isig/Petalisp
  254. Common Lisp for the Curious Clojurian – Alan Dipert – Scicloj meeting 19
    https://www.youtube.com/wat­ch?v=44Q9ew9JH_U
  255. Peter Norvig on Python
    https://serverhorror.wordpres­s.com/2010/10/19/peter-norvig-on-python/
  256. A History of the Common Lisp
    https://www.cleverism.com/skills-and-tools/common-lisp/
  257. Starting with Common Lisp in 2020
    http://dnaeon.github.io/starting-with-common-lisp-in-2020/
  258. JACL: JavaScript Assisted Common Lisp
    https://tailrecursion.com/JACL/

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.