Obsah
3. Význam speciálních symbolů T a nil
4. Uspořádané páry (cons cell)
5. CAR a CDR – operace pocházející z dob elektronkových sálových počítačů
6. Další operace odvozené od CAR a CDR
8. Vyhodnocování S-výrazů LISPem
9. Prefixový zápis aritmetických a relačních výrazů
10. Ale já chci používat „lidský“ zápis aritmetických výrazů!
11. Základní funkce pro práci se seznamy
12. Funkce jako základní stavební kámen programů, pojmenování uživatelských funkcí
14. Funkce s proměnným počtem parametrů
15. Nepovinné parametry funkcí
16. Parametry funkcí explicitně specifikované svým jménem
17. Repositář s demonstračními příklady
1. Základní datové typy Lispu
Je Matrix napsaný v LISPu nebo v Perlu?
Základními datovými typy, se kterými se v LISPu (samozřejmě včetně Common Lispu) pracuje, jsou atomy a uspořádané páry (tečka-dvojice). Atomy jsou z hlediska tohoto programovacího jazyka základními objekty, které není možné dále dělit (ostatně právě proto se také označují jako atomy), ale je je možné ukládat do uspořádaných párů. Atomy mohou být několika typů: jedná se především o symboly (například ABC. A právě symboly jsou pro LISP typické a proto byl tento jazyk použit v první generací „AI aplikací“). Dále patří mezi atomy čísla (42, 3.1415 atd. – některé interpretry jazyka LISP rozlišují celá čísla, čísla reálná, čísla komplexní a někdy též zlomky, tj. čísla racionální), řetězce („pokus“, „velmi dlouhý řetězec“), vestavěné funkce atd. Jak uvidíme dále, tak v reálných programech se atomy ukládají do seznamů, přičemž pro označení začátku a konce seznamu se používají kulaté závorky – levá závorka samozřejmě označuje začátek seznamu a pravá závorka jeho konec (viz navazující kapitoly). Prvky/elementy seznamu jsou od sebe odděleny alespoň jednou mezerou nebo koncem řádku, což mj. znamená, že seznam může být rozepsán na více řádcích (to je velmi důležité ve chvíli, kdy se pomocí seznamů reprezentují funkce).
2. Atomy
Atomy, o nichž jsme se ve stručnosti zmínili v úvodní kapitole, se rozdělují do několika skupin. Především se jedná o numerické datové typy, a to konkrétně o celá čísla (integers), zlomky (ratios), čísla s plovoucí řádovou čárkou (floats) a komplexní čísla (tedy dvojice s reálnou a imaginární hodnotou komplexního čísla). Celá čísla se dále dělí na fixnums (typicky v minulosti 16bitová celá čísla se znaménkem, na současných architekturách je bitová šířka větší, například 30 bitů nebo 62 bitů) a bignums (prakticky neomezený rozsah hodnot):
* 42 42 * -1 -1 * 3.14 3.14 * 1/2 1/2 * 999999999999999999999 999999999999999999999
Dále mezi atomy patří i symboly (symbols) a řetězce (strings):
* 'this-is-a-symbol THIS-IS-A-SYMBOL * "and this is a string" "and this is a string"
Interní reprezentace atomů se může lišit od jejich tištěné (či zapisované) reprezentace. Patrně nejvíce je to patrné na zlomcích (rational), které jsou zjednodušeny:
* 0/6 0 * 1/6 1/6 * 2/6 1/3 * 3/6 1/2 * 4/6 2/3 * 5/6 5/6 * 6/6 1
Pro zjištění (test), zda je argument atomem se používá predikát nazvaný jednoduše atom. Pokud je argument předaný tomuto predikátu skutečně atomem, vrátí se hodnota (symbol) T, v opačném případě se vrátí nil:
* (atom nil) T * (atom T) T * (atom 42) T * (atom 1/3) T * (atom "foo") T * (atom (list 1 2 3)) NIL * (atom '("uspořádaný" . "pár")) NIL
3. Význam speciálních symbolů T a nil
V Common Lispu (a taktéž i v mnoha dalších dialektech LISPu, nikoli však v tomto významu ve Scheme) se setkáme se speciálními symboly zapisovanými jako T a NIL (nebo též malými písmeny, popř. T/nil). Tyto symboly jsou vždy definovány již při inicializaci REPLu.
Jak se dozvíme v dalším textu, je zvláštním a v mnoha ohledech důležitým typem seznamu prázdný seznam, který neobsahuje žádné prvky (elementy) a proto je zapisován buď levou závorkou, za níž ihned následuje závorka pravá (mezi závorkami se tedy nenachází žádný atom ani další seznam, mohou se zde nacházet pouze mezery nebo konce řádků), nebo lze pro jeho zápis alternativně použít symbol nil, který je ekvivalentní prázdnému seznamu (současně se tímto symbolem označuje logická hodnota nepravda, tj. prázdný seznam se v logických výrazech vyhodnocuje na hodnotu false). Prázdný seznam a nil (či NIL) jsou zcela ekvivalentní, takže je jedno, který ze zápisů použijete. Současně je nil jediným objektem v LISPu, který je současně symbolem i seznamem.
Konkrétně v SBCL se všechny následující vstupy (zapsané tučně) vyhodnotí na NIL:
* '() NIL * () NIL * NIL NIL * nil NIL
Zatímco se nil v klasickém LISPu a tudíž i v Common Lispu používá mj. i pro reprezentaci nepravdy, tedy hodnoty false, je pro pravdivou hodnotu vyhrazen symbol T nebo t. Prakticky vždy se však setkáme s tím, že T je napsán verzálkou, zatímco zápis NIL/nil je psán různě (jak verzálkami, tak i minuskami; já budu používat zápis minuskami, který je kompatibilní i s dalšími programovacími jazyky, včetně těch mainstreamových).
Další důležitou vlastností těchto symbolů je, že nejsou modifikovatelné a že nelze vytvořit symbol se stejným jménem (který by teoreticky mohl přepsat původní symbol a dokonce i změnit jeho význam). Ostatně si to můžeme velmi snadno otestovat:
* (defvar T 42) debugger invoked on a SIMPLE-ERROR in thread #<THREAD "main thread" RUNNING {1000560083}>: Cannot proclaim a CONSTANT variable SPECIAL: T Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL. restarts (invokable by number or by possibly-abbreviated name): 0: [ABORT] Exit debugger, returning to top level. (SB-C::PROCESS-VARIABLE-DECLARATION T SPECIAL :SPECIAL) 0] 0
* (defvar NIL 42) debugger invoked on a SIMPLE-ERROR in thread #<THREAD "main thread" RUNNING {1000560083}>: Cannot proclaim a CONSTANT variable SPECIAL: NIL Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL. restarts (invokable by number or by possibly-abbreviated name): 0: [ABORT] Exit debugger, returning to top level. (SB-C::PROCESS-VARIABLE-DECLARATION NIL SPECIAL :SPECIAL) 0] 0
* (setf T 42) ; in: SETF T ; (SETF T 42) ; ==> ; (SETQ T 42) ; ; caught ERROR: ; T is a constant and thus can't be set. ; ; compilation unit finished ; caught 1 ERROR condition debugger invoked on a SB-INT:COMPILED-PROGRAM-ERROR in thread #<THREAD "main thread" RUNNING {1000560083}>: Execution of a form compiled with errors. Form: (SETQ T 42) Compile-time error: T is a constant and thus can't be set. Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL. restarts (invokable by number or by possibly-abbreviated name): 0: [ABORT] Exit debugger, returning to top level. ((LAMBDA ())) source: (SETF T 42)
4. Uspořádané páry (cons cell)
V úvodním textu jsme si řekli, že programovací jazyk LISP je založen na zpracování seznamů, ostatně odtud pochází i jeho název. Jak jsou však seznamy uloženy v operační paměti počítače a jak s nimi interpretry tohoto jazyka pracují? Základní interní strukturou, která je dostupná i programátorům aplikací v jazyce LISP, ve skutečnosti nejsou seznamy, ale takzvaný uspořádaný pár (neboli cons cell), jenž se někdy označuje i jako tečka-dvojice (dotted-pair). Tuto strukturu si můžeme představit jako dvojici unií (hodnota nebo ukazatel), přičemž každý z prvků této struktury může obsahovat přímo hodnotu (celé číslo), adresu atomu (třeba řetězce), adresu další tečka-dvojice nebo speciální hodnotu nil odpovídající v céčku hodnotě NULL či v Javě hodnotě null, tj. jedná se o speciální hodnotu, která interpretru říká, že daný ukazatel neobsahuje žádný odkaz (a současně značí false a aby toho nebylo málo, tak navíc i prázdný seznam, což uvidíme dále).
Uspořádaný pár je možné v LISPovských programech zapisovat formou dvojice výrazů (takzvaných S-výrazů) oddělených tečkou, které jsou uzavřeny do kulatých závorek (i když je pravda, že se s tímto zápisem v reálných programech příliš často nesetkáme, především z důvodu nepřehledného zápisu s velkým množstvím závorek):
Základní způsob zápisu uspořádaného páru (Common Lisp, na rozdíl od některých dalších LISPů, vyžaduje mezery na obou stranách tečky):
'(1 . 2) (1 . 2)
Musí se skutečně jednat o pár, což je hlídáno:
'(1 . 2 . 3) (1 . 2) -- Bad dotted pair
Složitější datová struktura tvořená rekurzivně zanořenými tečkovými páry:
'(1 . ((2 . 3) . 4)) (1 (2 . 3) . 4)
Zde se opět pokoušíme o vytvoření trojice:
'(1 . (2 . 3) . 4) (1 2 . 3) -- Bad dotted pair
V Common Lispu je vždy nutné interpretru zakázat pokus o vyhodnocení zadáním apostrofu:
'((1 . 2) . (3 . 4)) ((1 . 2) 3 . 4)
A nyní přichází na řadu velmi zajímavá vlastnost: rekurzivní tečkové páry zakončené dvojicí (atom . NIL) vytvoří seznam, resp. přesněji řečeno seznam je pouze syntaktickým cukrem pro takto vytvořenou datovou strukturu:
'(1 . (2 . (3 . NIL))) (1 2 3)
Dtto platí při použití prázdného seznamu jako ukončovacího prvku (prázdný seznam je při zpracování a vyhodnocování totožný s NIL):
'(1 . (2 . (3 . ()))) (1 2 3)
Konstrukce uspořádaného páru pomocí cons:
; vytvoření následující paměťové struktury ; . ; / \ ; A B ; (cons 'a 'b) (A . B) ; vytvoření jednoprvkového seznamu ze symbolu (atomu) ; vytvoří se tato struktura: ; . ; / \ ; A nil ; (cons 'a nil) (A) ; složitější příklady (cons '(a b) 'c) ((A B) . C) ; výsledkem je následující struktura ; . ; / \ ; . C ; / \ ; A . ; / \ ; B nil ; tento příklad je zajímavý, protože první i druhá část ; vytvořené tečka dvojice je sama o sobě seznamem (cons '(a b) '(c d)) ((A B) C D) ; asi nejtypičtější použití funkce cons: přidání ; prvku na začátek seznamu (cons 'a '(b c d)) (A B C D) ; výsledkem je následující struktura: ; . ; / \ ; A . ; / \ ; B . ; / \ ; C . ; / \ ; D nil ; pokus o přidání jediného prvku na konec seznamu ; pomocí funkce cons se nepodaří (cons '(a b c) 'd) ((A B C) . D) ; výsledkem je následující struktura ; . ; / \ ; . D ; / \ ; A . ; / \ ; B . ; / \ ; C nil ; která se seznamu podobá jen velmi vzdáleně ; funkce append je v tomto případě výhodnější, ; jen si musíme dát pozor na to, že se spojují ; dva seznamy, nikoli seznam a atom (append '(a b c) '(d)) (A B C D)
5. CAR a CDR – operace pocházející z dob elektronkových sálových počítačů
Pro přístup k informaci (atomu či další tečka dvojici), na kterou odkazuje první ukazatel tečka dvojice, se používá primitivní funkce s prapodivným jménem CAR, a pro přístup k informaci, na níž se odkazuje druhý ukazatel, lze použít funkci CDR (ovšem například celočíselné hodnoty jsou uloženy v páru přímo, bez nutnosti použití ukazatele). Common Lisp rozeznává i přece jen logičtější funkce FIRST a REST se stejným významem, jak mají CAR a CDR. Používat je možné oba způsoby pojmenování obou zmíněných operací, ovšem odkud se CAR a CDR vzaly?
První implementace programovacího jazyka LISP vznikla na sálovém počítači IBM 704, který byl založený na technologii elektronek a feritových pamětí. Tento počítač, přesněji řečeno jeho aritmeticko-logická jednotka, dokázal zpracovávat numerické či znakové hodnoty uložené ve slovech, jejichž šířka byla rovna 36 bitům (jednalo se buď o celá čísla se znaménkem, čísla uložená ve formátu plovoucí řádové čárky či o šestici znaků, z nichž každý byl uložen v šesti bitech). Operační paměť počítače IBM 704 byla z hlediska zpracování dat organizována po slovech o šířce 36 bitů (viz předchozí text), přičemž celkový počet těchto slov uložených v paměti mohl nabývat hodnot 4096, 8192 či 32768, což odpovídá postupně 18 kB, 36 kB a 144 kB (pro představu – v posledním případě musela feritová paměť obsahovat přes jeden milion feritových jadérek a několik desítek tisíc vodičů). Právě 36bitová šířka zpracovávaných slov měla velký vliv na způsob uložení datových struktur LISPu v operační paměti, jak si ostatně řekneme v navazujícím odstavci.
Obrázek 1: Sálový počítač IBM-704.
Způsob interní konstrukce uspořádaných párů v původní podobě vychází přímo z architektury počítače IBM 704, který používal 15bitové adresy, čemuž odpovídá v předchozím odstavci zmíněný maximální počet 32768 adresovatelných slov. V každém 36bitovém slově bylo možné uložit dvě 15bitové adresy nazvané podle svého významu address a decrement. Počítač IBM 704 obsahoval instrukce, pomocí nichž bylo možné jednu z částí address či decrement z 36bitového slova získat a uložit ji do index registru, jenž se používal při adresování operandů strojových instrukcí. Tvůrci první implementace jazyka LISP tuto možnost využili – ukládali tečka-dvojice do 36bitového slova rozděleného na čtyři části: prefix (3 bity), address (15 bitů), decrement (15 bitů) a tag (3 bity). Původně v LISPu existovaly čtyři funkce, pomocí nichž bylo možné získat jednu ze čtyř částí 36bitového slova představujícího tečka dvojici:
Primitivní funkce | Význam |
---|---|
CAR | Contents of the Address part of Register |
CDR | Contents of the Decrement part of Register |
CPR | Contents of the Prefix part of Register |
CTR | Contents of the Tag part of Register |
Z těchto funkcí nakonec ve finální verzi LISPu zůstaly pouze funkce CAR a CDR, které můžeme nalézt v prakticky každém dialektu tohoto jazyka. Druhou oblastí, v níž se projevila architektura počítače IBM 704, byl způsob správy paměti. Původně chtěli tvůrci LISPu použít metodu počítání referencí na objekty (reference counting), ovšem to bylo poměrně problematické – v každém 36bitovém slově zbylo pouhých šest bitů (prefix a tag), navíc rozmístěných takovým způsobem, že jejich sloučení bylo poměrně obtížné. Použití čítačů referencí by tedy vedlo k nutnosti změny významu všech bitů v 36bitovém slově, do čehož se autorům LISPu evidentně nechtělo :-) Proto namísto čítače referencí implementovali automatickou správu paměti pomocí plnohodnotného garbage collectoru, který dokázal pracovat korektně i v případech, kdy se vytvořila datová struktura obsahující cyklus (viz též následující části tohoto seriálu).
6. Další operace odvozené od CAR a CDR
Kromě funkcí CAR a CDR vznikly i další „variace na dané téma“, například funkce CAAR odpovídající volání (CAR (CAR …)). V následující tabulce jsou často použité varianty funkcí CAR a CDR vypsány (totéž ovšem nelze udělat s funkcemi FIRST a REST; zde je patrné, jak důležité je v informatice správné pojmenování):
Funkce | Výslovnost | Alternativní jméno |
---|---|---|
CAR | kar | FIRST |
CDR | cou der | REST |
CAAR | ka ar | |
CADR | kae der | SECOND |
CDAR | cou dar | |
CDDR | cou dih-der | |
CAAAR | ka a-ar | |
CAADR | ka ae-der | |
CADAR | ka dar | |
CADDR | ka dih-der | THIRD |
CDAAR | cou da-ar | |
CDADR | cou dae-der | |
CDDAR | cou dih-dar | |
CDDDR | cou did-dih-der | |
CADDDR | ka dih-dih-der | FOURTH |
7. LISP = LISt Processor
S využitím uspořádaných párů zmíněných v páté kapitole je možné vytvořit klasický seznam následujícím způsobem: první ukazatel každého n-tého uspořádaného páru odkazuje na n-tý prvek seznamu (například na atom, řekněme na číslo 42 či symbol A), druhý ukazatel se pak odkazuje na další (n plus první) uspořádaný pár. Speciálním případem je poslední uspořádaný pár, jehož druhý ukazatel obsahuje výše uvedenou speciální hodnotu nil. Z následujícího příkladu (obsahujícího ekvivalentní datové struktury) je patrné, že použití syntaxe pro zápis seznamů je pro uživatele přehlednější a současně i kratší, než explicitní zápis uspořádaných párů; ovšem právě znalost vnitřní reprezentace seznamů pomocí uspořádaných párů nám umožňuje pochopit, jak pracují některé základní funkce, včetně již zmíněných funkcí CAR a CDR:
; seznam zapsaný s využitím uspořádaných párů '(1 . (2 . (3 . (4 . (5 . nil))))) (1 2 3 4 5) ; běžný způsob zápisu seznamu '(1 2 3 4 5) (1 2 3 4 5)
Takto zapsané seznamy jsou z hlediska jazyka Common Lisp ekvivalentní:
(equal '(1 . (2 . (3 . (4 . (5 . nil))))) '(1 2 3 4 5)) T
Interní struktura seznamu uloženého v paměti bude vypadat takto (bude tedy přesně odpovídat prvnímu zápisu):
; . ; / \ ; 1 . ; / \ ; 2 . ; / \ ; 3 . ; / \ ; 4 . ; / \ ; 5 nil
Na tomto místě poznamenejme, že další struktury vytvořené s využitím rekurzivně zanořených uspořádaných párů není možné převést na běžné seznamy. Například jednoduchý binární strom se třemi úrovněmi a čtyřmi listy lze reprezentovat buď pomocí uspořádaných párů (v operační paměti se vytvoří skutečná obdoba binárního stromu), popř. je možné tuto datovou strukturu „simulovat“ pomocí seznamů (ovšem v tomto případě bude paměťová náročnost vyšší kvůli nutnosti ukončení všech podseznamů uspořádaným párem obsahujícím ve svém druhém ukazateli hodnotu nil):
; binární strom se třemi úrovněmi a čtyřmi listy vytvořený pomocí tečka dvojic '((A . B) . (C . D)) ((A . B) C . D) ; interní podoba této struktury v operační paměti: ; . ; / \ ; . . ; / \ / \ ; A B C D ; binární strom vytvořený pomocí LISPovských seznamů '((A B) (C D)) ((A B) (C D)) ; interní podoba této struktury v operační paměti: ; . ; / \ ; / \ ; / \ ; / \ ; . . ; / \ / \ ; A . . nil ; / \ / \ ; B nil C . ; / \ ; D nil
(defvar c (list 1 2 3 4)) C c (1 2 3 4) ; nutno použít pro zabránění nekonečnému výpisu (setq *print-circle* T) T ; poslední vazba - vytvoření cyklu v grafu (setf (cdr (last c)) c) #1=(1 2 3 4 . #1#) c #1=(1 2 3 4 . #1#)
8. Vyhodnocování S-výrazů LISPem
Programy jsou v klasickém LISPu a tudíž i v Common Lispu zapisovány – podobně jako data, se kterými tyto programy pracují – taktéž pomocí seznamů, přičemž prvním prvkem takového seznamu je symbol nějaké funkce. Interpretry LISPu jsou někdy (dodejme, že poněkud nepřesně) označovány zkratkou REPL, která naznačuje, jakým způsobem jsou programy, resp. jednotlivé výrazy, ze kterých se programy skládají, zpracovávány: Read–Eval–Print–Loop. Ve skutečnosti však mnoho implementací LISPu obsahuje i plnohodnotné překladače, které mnohdy produkují optimalizovaný kód srovnatelný například s výsledky Céčkových překladačů (což je případ Common Lispu).
Dnes si způsob zápisu programů popíšeme do větších podrobností. Základem interpretace každého LISPovského programu je načtení a rozpoznání jednoho výrazu (read), vyhodnocení tohoto výrazu (eval) a následný tisk výsledné hodnoty výrazu na standardní výstup (print). Pravidla pro vyhodnocení výrazů (někdy se též můžeme setkat s termínem „vyhodnocení forem“) jsou v LISPu velmi jednoduchá a přímočará, na rozdíl od mnoha jiných programovacích jazyků (vlastně na rozdíl od všech mainstreamových jazyků). Tato pravidla lze ve zjednodušené podobě sepsat do pouhých několika bodů:
- čísla a řetězce jsou literály, které jsou vyhodnoceny samy na sebe (což je logické – jedná se o dále nedělitelné objekty, tedy o nám již známéatomy)
- hodnotou symbolu je objekt, který je na tento symbol navázán (analogie z jiných programovacích jazyků – hodnotou proměnné zapsané svým jménem je hodnota uložená do proměnné)
- seznamy jsou vyhodnocovány tak, že se první prvek seznamu chápe jako jméno funkce (či jako jméno speciální formy), které je předán zbytek seznamu jako parametry této funkce (nebo speciální formy)
- v případě, že seznam obsahuje podseznamy, jsou tyto podseznamy vyhodnoceny nejdříve, přičemž úroveň rekurzivního zanořování při vyhodnocování podseznamů není teoreticky omezena (tj. podseznamy lze vnořovat do téměř libovolné úrovně)
Ukažme si nyní vyhodnocování výrazů na několika velmi jednoduchých příkladech. První řádek uvedený pod poznámkou (uvozenou znakem ;) představuje text zapsaný uživatelem na klávesnici, řádek druhý vypisuje samotný LISP (tedy například SBCL):
; vyhodnocení číselné hodnoty (konstanty) 42 42 ; vyhodnocení číselné hodnoty (konstanty), ovšem ve tvaru zlomku 1/3 1/3 ; zlomky se automaticky zjednodušují 10/2 5 ; vyhodnocení speciálního symbolu nil nil NIL ; vyhodnocení speciálního symbolu T T T ; nil je současně zcela ekvivalentní prázdnému seznamu (což již víme) () NIL ; vyhodnocení řetězce "www.root.cz" "www.root.cz" ; vyhodnocení seznamu obsahujícího jako první prvek funkci (max 10 20) 20 ; vyhodnocení seznamu obsahujícího další seznamy (každý podseznam v tomto případě znamená volání funkce) (max (min 10 20) (min 30 40)) 30 ; zákaz vyhodnocení seznamu speciální formou quote (quote (max 10 20)) (MAX 10 20) ; zákaz vyhodnocení seznamu zkrácenou speciální formou quote '(max 10 20) (MAX 10 20)
9. Prefixový zápis aritmetických a relačních výrazů
Zatímco v naprosté většině „mainstreamových“ programovacích jazyků, jakými jsou například klasické Céčko, Java, JavaScript, Go či Python, se aritmetické a logické výrazy zapisují v takzvané infixové notaci, při níž jsou binární operátory zapisovány mezi dvojici operandů, tvůrci jazyka LISP se od tohoto způsobu zápisu distancovali – namísto toho jsou v LISPu (a tudíž i v Common Lispu) všechny základní aritmetické i logické (a samozřejmě též relační) operace zapisovány jako volání funkcí (přesněji řečeno se skutečně jedná o funkce, dokonce o funkce variadické), tj. vždy v prefixové podobě. Důvodů, proč byla zvolena tato forma zápisu výrazů, je více. Prvním důvodem je fakt, že syntaxe LISPu byl navrhována s tím, že později dojde k jejím dalším modifikacím, tj. samotná syntaxe nebyla pro tvůrce tohoto programovacího jazyka tak prioritní jako jeho sémantika (paradoxní přitom je, že se nakonec syntaxe LISPu nezměnila, takzvané M-výrazy se nedočkaly většího rozšíření, podobně jako další snahy o úpravu syntaxe LISPu tak, aby se eliminovalo množství závorek či právě prefixový zápis aritmetických výrazů).
Druhý důvod spočíval v tom, že zavedení infixových operátorů by do programovacího jazyka zavádělo další komplikace: musely by se například řešit a přesně specifikovat priority operací (a u některých operací i jejich asociativita), se zapsanými výrazy by se složitěji prováděly různé symbolické manipulace (integrace, derivace, zjednodušování výrazů), infixové operátory by nebylo možné předávat jako parametry do jiných funkcí atd. Vzhledem k tomu, že aritmetické operátory jsou v LISPu zapisovány jako volání funkcí, musí se znak či jméno příslušného operátoru uvádět ve vyhodnocovaném seznamu na prvním místě, podobně jako jméno jakékoli jiné funkce. Všechny dílčí podvýrazy se samozřejmě vyhodnocují dříve než celý výraz, což plně koresponduje s pravidly, která jsme si uvedli v předchozí kapitole (podvýraz je zapsán formou volání nějaké funkce). Většina aritmetických funkcí není omezena pouze na dva parametry, což znamená, že je například možné zavoláním jedné funkce nazvané + sečíst i více než dvě numerické hodnoty:
; unární varianty (+ 1) 1 (- 1) -1 ; převrácená hodnota (/ 2) 1/2 ; identita (* 2) 2 ; začneme pozvolna jako na základní škole :-) (+ 1 1) 2 ; operace rozdílu - druhý argument funkce je odečten od prvního (- 1 2) -1 ; součet řady čísel (+ 1 2 3 4 5 6 7 8 9 10) 55 ; níže uvedený výraz v infixové notaci odpovídá: 1-2-3-4-5....-10: (- 1 2 3 4 5 6 7 8 9 10) -53 ; POZOR - závorky v LISPu nemají mnoho společného ; s vyjádřením priority aritmetických operací ; (nelze je použít tak volně jako například v céčku) (* (+ 1 2) (+ 3 4)) 21 (+ (* 1 2) (* 3 4)) 14 ; Některé implementace LISPu, například i zde popisovaný Common Lisp, ; dokážou pracovat se zlomky, tj. snaží se racionální ; čísla vyjádřit formou zlomku (ideální jazyk do škol :-) (/ 1 2) 1/2 (/ 1 2 3) 1/6 (/ 3 2) 3/2 ; zkusíme výpočet složitějšího zlomku (/ (+ 1 2) (+ 3 4)) 3/7 ; neracionální (reálná) čísla se vypisují tak, jak to ; známe z ostatních programovacích jazyků (samozřejmě ; v případě speciálních požadavků programátora lze použít ; různé formátovací funkce na úpravu výstupu) (* 0.3 (/ (+ 1 2) (+ 3 4))) 0.12857144
Programovací jazyk LISP obsahuje i úplnou sadu relačních operátorů, které v závislosti na hodnotách předaných parametrů (operandů) vrací hodnotu T (pravda) či nil (nepravda). Povšimněte si, že konstanta nil má v LISPu poměrně velké množství různých významů:
; porovnání dvou číselných hodnot ; relace "menší než" (< 1 2) T ; relace "větší než" (> 1 2) NIL ; postupné porovnání hodnot (první-druhá atd.) (< 1 2 3 4) T (< 3 1 2 4 5) NIL ; relace "menší nebo rovno" (<= 1 2) T ; relace "větší nebo rovno" (>= 1 2) NIL ; porovnání dvou výrazů na ekvivalenci (equal 1 2) NIL (equal 1 1) T ; podvýrazy se nejprve vyhodnotí a posléze se porovnají ; vyhodnocené výsledky (v tomto případě dva atomy) (equal (+ 1 1) (/ 4 2)) T ; na ekvivalenci lze porovnávat i seznamy, nikoli pouze atomy (equal '(1 2) '(1 2)) T (equal '(1 2) '(2 1)) NIL ; nil se sice v některých pohledech podobá klíčovému slovu ; NULL z SQL ovšem způsob vyhodnocování této konstanty ; v LISPovských výrazech je poněkud odlišný (equal nil nil) T
10. Ale já chci používat „lidský“ zápis aritmetických výrazů!
V případě, že se má v nějaké LISPovské aplikaci použít větší množství výpočtů, jež by mohly být při použití prefixové notace nepřehledné, je možné použít hned několik knihoven, které slouží k transformaci výrazů zapsaných infixově (tedy „tak jak nás to učili ve škole“, včetně priorit) do prefixové podoby. Některé z těchto knihoven dokonce dokážou výrazy různým způsobem zjednodušovat či kombinovat. Například knihovna (spíše knihovnička, protože se skládá jen z několika málo funkcí a maker) cmu-infix určená pro Common Lisp nabízí uživatelům funkci #I (ano, i takové názvy funkcí je možné v LISPu použít, a to z toho důvodu, že znaky -, # či > nemají díky absenci operátorů žádný speciální význam), kterou lze použít způsobem ukázaným na následujícím jednoduchém příkladu. Povšimněte si, že mezi operandy a operátory nemusí být zapsány mezery, protože knihovna provádí vlastní parsing výrazů (jiné podobně zaměřené knihovny ovšem mezery vyžadují).
Výpočet délky vektoru, popř. přepony pravoúhlého trojúhelníku:
#I( sqrt(a^^2 + b^^2) ))
Použít lze i relační a logické operátory:
#I( n != 0 and (n & (n - 1)) == 0 ))
A konečně si ukažme třetí příklad získaný z oficiální dokumentace této knihovny. Jedná se o definici funkce pro maticový součin:
(defun matmul (A B) "Compute C = A * B for matrices A and B." (let* ((m (array-dimension A 0)) (n (array-dimension A 1)) (q (array-dimension B 1)) (C (make-array (list m q) :initial-element 0))) (loop :for i :below m :do (loop :for k :below q :do (loop :for j :below n :do #I( C[i, k] += A[i, j] * B[j, k] )))) C))
11. Základní funkce pro práci se seznamy
Z popisu programovacího jazyka LISP uvedeného v předchozích kapitolách již víme, jakým způsobem se v tomto jazyku zapisují seznamy a dokonce známe i dvě funkce sloužící pro získání prvního prvku seznamu (car neboli first) a naopak zbytku seznamu bez jeho prvního prvku (cdr neboli rest). Vzhledem k tomu, že práce se seznamy tvoří poměrně podstatnou část činnosti programátorů při psaní aplikací, obsahuje programovací jazyk LISP ve své základní knihovně i mnohé další funkce, s jejichž pomocí je možné se seznamy různým způsobem manipulovat. Některé z nejčastěji používaných funkcí jsou vypsány v následující tabulce. Jedná se o funkce dostupné v dialektu Common Lisp, proto se v některých jiných dialektech můžete setkat s odlišným pojmenování některých funkcí (například se namísto predikátu LISTP používá LIST?):
Jméno funkce | Význam |
---|---|
CONS | základní funkce – přidání dalšího elementu do seznamu, popř. konstrukce uspořádaného páru (dvojice) |
LAST | vrací poslední prvek seznamu |
NTH | vrací n-tý prvek seznamu |
LENGTH | zjištění délky seznamu |
LIST | vytvoří nový seznam |
APPEND | kombinace více seznamů |
LISTP | predikát, který vrací T nebo nil v závislosti na tom, zda je parametrem seznam či jiný objekt |
Ukažme si nyní použití výše uvedených funkcí na jednoduchých demonstračních příkladech. Použití apostrofu před čtyřprvkovým seznamem opět zabraňuje tomu, aby se interpretr snažil seznam vyhodnotit, tj. volat (neexistující) funkci a s parametry b, c, d.
; funkci car již známe (car '(a b c d)) A ; stejně jako funkci cdr (cdr '(a b c d)) (B C D) ; cadr odpovídá výrazu (car (cdr seznam)) (cadr '(a b c d)) B (car (cdr '(a b c d))) B (cdar '(a b c d)) nelze vyhodnotit, protože se volá funkce CDR na atom A ; pozor - zde Common Lisp vrací jednoprvkový seznam (last '(a b c d)) (D) ; prvky seznamu jsou počítány od nuly (nth 0 '(a b c d)) A (nth 2 '(a b c d)) C ; pokus o přístup k neexistujícímu prvku seznamu (nth 4 '(a b c d)) NIL
Zjištění délky seznamu:
(length '(a b c d)) 4
Konstrukce seznamů může být na první pohled poněkud složitější, zejména v případě použití funkce cons (constructor). Když si však uvědomíme, že tato funkce nedělá nic jiného než konstrukci uspořádaného páru z obou předaných parametrů, je její chování zřejmé.
Následují příklady použití predikátu LISTP. Vzhledem k tomu, že je LISP dynamicky typovaný jazyk, používají se predikáty v něm napsaných aplikacích poměrně často:
; test, zda je jednička (tj. atom) seznamem (listp 1) NIL ; test, zda je výsledek součtu dvou čísel seznamem (listp (+ 1 2)) NIL ; test, zda je symbol A (atom) seznamem (listp 'A) NIL ; (a b c d) je zcela jistě seznam (listp '(a b c d)) T ; i prázdný seznam je seznam :-) (listp '()) T ; prázdný seznam a nil jsou ekvivalentní (listp 'nil) T ; nil se vyhodnocuje samo na sebe a navíc je ; ekvivalentní s prázdným seznamem - z toho ; vyplývá, že se před něj nemusí psát apostrof, ; protože se nemusíme "bát" vyhodnocení nil ; (trošku se nám to začíná komplikovat :-) (listp nil) T
12. Funkce jako základní stavební kámen programů, pojmenování uživatelských funkcí
Naprostým základem při tvorbě každé jen trošku rozsáhlejší aplikace je dekompozice problému na menší části, které je možné realizovat snadněji, protože se výchozí problém více konkretizuje (a přibližuje se tak jak možnostem použitého programovacího jazyka, tak i schopnosti vývojáře problém naprogramovat :-). V programovacím jazyku Common Lisp se, podobně jako v mnoha dalších imperativních a především funkcionálních programovacích jazycích, pro rozklad problému na menší části používají uživatelsky definované funkce, a to jak funkce pojmenované (navázané na nějaký symbol – jméno), tak i funkce anonymní (tento typ funkcí je představován lambda výrazy).
V této kapitole si popíšeme způsob tvorby pojmenovaných funkcí a v kapitole třinácté se budeme zabývat problémem tvorby funkcí anonymních, s čímž souvisí i problematika vytvoření a následného použití lokálních proměnných. Možná by na tomto místě bylo vhodné připomenout, že z čistě teoretického hlediska by se měly anonymní funkce popsat dříve než funkce pojmenované, protože právě anonymní funkce tvoří základ pro vytváření jak funkcí pojmenovaných, tak i lokálních proměnných (a mnoha dalších užitečných jazykových konstrukcí). Vytvoření uživatelské pojmenované funkce je v programovacím jazyku Common Lisp velmi jednoduché – použije se makro defun, za nímž se zapíše jméno nově vytvářené funkce i jména jejích formálních parametrů (ta jsou zapsána v seznamu). Za tímto seznamem následuje tělo funkce, tj. výraz či sekvence výrazů, které se mají vyhodnotit (v těchto výrazech je samozřejmě možné používat formální parametry funkce).
Hodnota posledního vyhodnoceného výrazu se (většinou) stává i návratovou hodnotou celé funkce, což mj. znamená, že všechny předchozí výrazy musí mít vedlejší efekt, jinak je jejich volání (použití v těle funkce) vlastně zbytečné.
Formálně vypadá vytvoření nové funkce následovně:
(defun [jméno funkce] ([formální parametry]) [tělo funkce])
Naproti tomu ve Scheme se používá „jiné uzávorkování“ a namísto makra defun se používá speciální forma define:
(define ([jméno funkce] [formální parametry]) [tělo funkce])
Definice konkrétní pojmenované funkce bez parametrů (vrací konstantu 42) a její následné zavolání:
(defun foo () 42) (print (foo))
Postup vytvoření uživatelské funkce s jedním parametrem a jejího následného použití (zavolání):
(defun foo (message) (print message)) (foo "Hello")
Samozřejmě je možné vytvořit i funkci víceparametrickou:
(defun bar (message other) (print message)) (bar "Hello" "world")
Zápis na jednom, resp. na více řádcích:
(defun add (x y) (+ x y)) (defun mul (x y) (* x y)) (print (add 1 2)) (print (mul 6 7))
Ve funkci je možné vytvořit blok s lokálními proměnnými formou let:
(defun add+1 (x y) (let ((result (+ x y))) (+ 1 result))) (print (add+1 1 2))
V programovacím jazyku Common Lisp lze vytvářet i funkce, v jejichž názvu se nachází různé nealfanumerické znaky. Je to ostatně logické, protože se jedná o jeden z těch jazyků (a je jich překvapivě velké množství), v nichž neexistují ani operátory (zapisované většinou právě pomocí nealfanumerických znaků) ani většina dalších speciálních syntaktických konstrukcí. V předchozích částech tohoto seriálu jsme si již ukázali některé predikáty, u nichž je obvyklé, že jsou jejich jména ukončena znakem otazník (?, minimálně ve Scheme).
13. Anonymní funkce
Kromě pojmenovaných funkcí ve stručnosti popsaných v předchozí kapitole je možné v programovacím jazyce Common Lispu, podobně jako i v klasickém Scheme, ale i mnoha dalších jazycích umožňujících funkcionální programování, vytvářet a používat takzvané funkce anonymní. Tyto funkce, které je možné s výhodou využít například při zápisu iterací nad prvky seznamů či při omezování oblasti platnosti proměnných, se vytváří s využitím speciální formy lambda, jejíž název je odvozen ze slavné Churchovy teorie Lambda kalkulu, která má poměrně velký význam jak v teoretické informatice, tak i v dalších odvětvích informatiky (viz též odkazy uvedené v poslední kapitole). Samotný zápis anonymní funkce se příliš neliší od zápisu funkce pojmenované – jediný syntaktický rozdíl spočívá v tom, že se při zápisu speciální formy lambda nikde neuvádí jméno funkce, pouze seznam (jména) formálních parametrů, za nimiž následuje tělo funkce:
(print ((lambda () "no parameters"))) (print ((lambda (a) (list "one parameter" a)) 10)) (print ((lambda (a b) (list "two parameters" a b)) 10 20))
Po vyhodnocení těchto tří příkazů bychom měli získat tyto výsledky:
"no parameters" ("one parameter" 10) ("two parameters" 10 20)
Používat je možné i nepovinné parametry, anonymní funkce s proměnným počtem parametrů atd. (viz též navazující kapitoly s podrobnějším popisem). Podívejme se nyní jen na způsob zápisu:
(print ((lambda (a b &optional c d) (list "two or more parameters" a b c d)) 10 20)) (print ((lambda (a b &optional c d) (list "two or more parameters" a b c d)) 10 20 30)) (print ((lambda (a b &optional c d) (list "two or more parameters" a b c d)) 10 20 30 40)) (print ((lambda (a b &rest values) (list "two or more parameters" a b values)) 10 20)) (print ((lambda (a b &rest values) (list "two or more parameters" a b values)) 10 20 30)) (print ((lambda (a b &rest values) (list "two or more parameters" a b values)) 10 20 30 40))
14. Funkce s proměnným počtem parametrů
V Common Lispu je možné s využitím formy defun nadefinovat pojmenovanou funkci akceptující proměnný (tj. v krajním případě i nulový) počet parametrů, z nichž je při volání funkce automaticky vytvořen seznam, se kterým je možné v těle funkce libovolným způsobem manipulovat. Syntakticky vypadá definice takové funkce následovně:
(defun jméno_funkce (&rest values) [tělo funkce])
Nebo lze kombinovat povinné parametry a proměnný počet parametrů:
(defun jméno_funkce (povinné parametry &rest values) [tělo funkce])
Podívejme se na jednoduchý příklad:
(defun show-values (a b &rest values) (print values) (print (list a b values)) (terpri)) (show-values 1 2) (show-values 1 2 3) (show-values 1 2 3 4)
S výsledky:
NIL (1 2 NIL) (3) (1 2 (3)) (3 4) (1 2 (3 4))
Odlišný způsob zpracování proměnných parametrů:
(defun show-values (a b &rest values) (let ((l (append (list a b) values))) (print values) (print l) (terpri))) (show-values 1 2) (show-values 1 2 3) (show-values 1 2 3 4)
Tentokrát s výsledky:
NIL (1 2) (3) (1 2 3) (3 4) (1 2 3 4)
A konečně si uveďme příklad, v němž se všechny parametry postupně vypíšou v dolist (což je vlastně smyčka typu for-each):
(defun show-values (&rest values) (dolist (value values) (print value)) (terpri)) (show-values) (show-values 1) (show-values 1 2) (show-values 1 2 3) (show-values 1 2 3 4) (show-values 1 2 3 4 5)
(nic) 1 1 2 1 2 3 1 2 3 4 1 2 3 4 5
(define (jméno funkce . parametr) [tělo funkce])
15. Nepovinné parametry funkcí
Při deklaraci funkcí můžeme použít i takzvané nepovinné parametry. Pokud se při volání funkce neuvede hodnota takového parametru, bude za ni dosazena výchozí hodnota. Nepovinné parametry se poznají snadno – jsou zapsány za &optional a to buď jen jménem nebo formou seznamu o dvou prvcích. Prvním prvkem seznamu je jméno parametru, druhým prvkem pak výchozí hodnota nepovinného parametru.
Použití nepovinných parametrů, jejichž výchozí hodnota bude nil:
(defun show-values (a b &optional c d) (print c) (print d) (print (list a b c d)) (terpri)) (show-values 1 2) (show-values 1 2 3) (show-values 1 2 3 4)
Výsledky:
NIL NIL (1 2 NIL NIL) 3 NIL (1 2 3 NIL) 3 4 (1 2 3 4)
Použití nepovinných parametrů i s nastavenou výchozí hodnotou:
(defun show-values (a b &optional (c -1) (d "nic")) (print c) (print d) (print (list a b c d)) (terpri)) (show-values 1 2) (show-values 1 2 3) (show-values 1 2 3 4)
Výsledky získané po spuštění:
-1 "nic" (1 2 -1 "nic") 3 "nic" (1 2 3 "nic") 3 4 (1 2 3 4)
Proměnný počet parametrů lze zkombinovat s funkcí s proměnným počtem parametrů, ovšem přesně v tomto pořadí:
(defun show-values (a b &optional c d &rest values) (print c) (print d) (print values) (print (list a b c d values)) (terpri)) (show-values 1 2) (show-values 1 2 3) (show-values 1 2 3 4) (show-values 1 2 3 4 5) (show-values 1 2 3 4 5 6)
S výsledky:
NIL NIL NIL (1 2 NIL NIL NIL) 3 NIL NIL (1 2 3 NIL NIL) 3 4 NIL (1 2 3 4 NIL) 3 4 (5) (1 2 3 4 (5)) 3 4 (5 6) (1 2 3 4 (5 6))
Další varianta, tentokrát se specifikací výchozí hodnoty nepovinných parametrů:
(defun show-values (a b &optional (c -1) (d "nic") &rest values) (print c) (print d) (print values) (print (list a b c d values)) (terpri)) (show-values 1 2) (show-values 1 2 3) (show-values 1 2 3 4) (show-values 1 2 3 4 5) (show-values 1 2 3 4 5 6)
Výsledky:
-1 "nic" NIL (1 2 -1 "nic" NIL) 3 "nic" NIL (1 2 3 "nic" NIL) 3 4 NIL (1 2 3 4 NIL) 3 4 (5) (1 2 3 4 (5)) 3 4 (5 6) (1 2 3 4 (5 6))
Ovšem tato kombinace již není dovolena (celkem logicky):
(defun show-values (a b &rest values &optional (c -1) (d "nic")) (print c) (print d) (print values) (print (list a b c d values)) (terpri)) (show-values 1 2) (show-values 1 2 3) (show-values 1 2 3 4) (show-values 1 2 3 4 5) (show-values 1 2 3 4 5 6)
Při pokusu o vyhodnocení nastane chyba:
; file: /tmp/ramdisk/function_optional_rest_params_3.lisp ; in: DEFUN SHOW-VALUES ; (DEFUN SHOW-VALUES (A B &REST VALUES &OPTIONAL (C -1) (D "nic")) ; (PRINT C) ; (PRINT D) ; (PRINT VALUES) ; (PRINT (LIST A B C D VALUES)) ; (TERPRI)) ; ; caught ERROR: ; misplaced &OPTIONAL in lambda list: (A B &REST VALUES &OPTIONAL (C -1) ; (D "nic")) Unhandled SB-INT:SIMPLE-PROGRAM-ERROR in thread #<SB-THREAD:THREAD "main thread" RUNNING {1000560083}>: misplaced &OPTIONAL in lambda list: (A B &REST VALUES &OPTIONAL (C -1) (D "nic"))
#lang racket/base (define (inc x [delta 1]) (+ x delta)) (display (inc 10)) (newline) (display (inc 10 20)) (newline)
16. Parametry funkcí explicitně specifikované svým jménem
Kromě povinných a nepovinných parametrů, popř. volitelného počtu parametrů existují ještě další způsoby, jakými je možné funkcím předávat hodnoty, které mají funkce zpracovávat. Jednou z dalších možností jsou takzvané keywords parametry, což jsou parametry s explicitně specifikovaným jménem. Jména těchto parametrů se zadávají za &key a liší se i způsob volání funkce.
Příklad použití:
(defun show-values (&key a b c d) (print (list a b c d))) (show-values :a 1 :b 2 :c 3 :d 4) (show-values :b 1 :a 2 :d 3 :c 4) (show-values :d 1 :c 2 :b 3 :a 4)
Výsledek:
(1 2 3 4) (2 1 4 3) (4 3 2 1)
Při volání funkce není pořadí pojmenovaných parametrů pevně dané a parametry lze vynechat:
(defun show-values (&key a b c d) (print (list a b c d))) (show-values) (show-values :a 1) (show-values :b 2) (show-values :a 1 :b 2) (show-values :c 3 :d 4)
Výsledek:
(NIL NIL NIL NIL) (1 NIL NIL NIL) (NIL 2 NIL NIL) (1 2 NIL NIL) (NIL NIL 3 4)
Kombinace volitelných a pojmenovaných parametrů:
(defun show-values (a b &optional c d &key e f) (print (list a b c d e f))) (show-values 1 2) (show-values 1 2 3) (show-values 1 2 3 4) (show-values 1 2 :e 100) (show-values 1 2 :e 100 :f 200) (show-values 1 2 3 4 :e 100) (show-values 1 2 3 4 :f 200) (show-values 1 2 3 4 :e 100 :f 200)
Tentokrát s výsledkem:
(1 2 NIL NIL NIL NIL) (1 2 3 NIL NIL NIL) (1 2 3 4 NIL NIL) (1 2 :E 100 NIL NIL) (1 2 :E 100 NIL 200) (1 2 3 4 100 NIL) (1 2 3 4 NIL 200) (1 2 3 4 100 200)
#lang racket/base (define (transform x #:scale scale #:offset offset) (+ offset (* x scale))) (display (transform 10 #:offset 0 #:scale 1)) (newline) (display (transform 10 #:offset -100 #:scale 1)) (newline) (display (transform 10 #:offset -100 #:scale 100)) (newline)
17. Repositář s demonstračními příklady
Zdrojové kódy všech minule i 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:
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):
- 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/ - 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/ - Interlisp aneb oživujeme dinosaura
https://www.root.cz/clanky/interlisp-aneb-ozivujeme-dinosaura/ - 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/ - 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/ - Common Lisp: žralok mezi programovacími jazyky
https://www.root.cz/clanky/common-lisp-zralok-mezi-programovacimi-jazyky/
Články o Elispu:
- Ú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/ - Úpravy Emacsu s Emacs Lisp: základní konstrukce jazyka
https://www.root.cz/clanky/upravy-emacsu-s-emacs-lisp-zakladni-konstrukce-jazyka/ - Ú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/ - Ú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/ - Úpravy Emacsu s Emacs Lisp: dokončení popisu Emacs Lispu
https://www.root.cz/clanky/upravy-emacsu-s-emacs-lisp-dokonceni-popisu-emacs-lispu/ - Ú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é):
- Peter Seibel
„Practical Common Lisp“
2009 - Paul Graham
„ANSI Common Lisp“
1995 - Gerald Gazdar
„Natural Language Processing in Lisp: An Introduction to Computational Linguistics“
1989 - Peter Norvig
„Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp“
1991 - Alex Mileler et.al.
„Clojure Applied: From Practice to Practitioner“
2015 - „Living Clojure: An Introduction and Training Plan for Developers“
2015 - Dmitri Sotnikov
„Web Development with Clojure: Build Bulletproof Web Apps with Less Code“
2016 - McCarthy
„Recursive functions of symbolic expressions and their computation by machine, part I“
1960 - R. Kent Dybvig
„The Scheme Programming Language“
2009 - Max Hailperin
„Concrete Abstractions“
1998 - Guy L. Steele
„History of Scheme“
2006, Sun Microsystems Laboratories - Kolář J., Muller K.:
„Speciální programovací jazyky“
Praha 1981 - „AutoLISP Release 9, Programmer's reference“
Autodesk Ltd., October 1987 - „AutoLISP Release 10, Programmer's reference“
Autodesk Ltd., September 1988 - McCarthy, John; Abrahams, Paul W.; Edwards, Daniel J.; Hart, Timothy P.; Levin, Michael I.
„LISP 1.5 Programmer's Manual“
MIT Press. ISBN 0 262 130 1 1 4 - Carl Hewitt; Peter Bishop and Richard Steiger
„A Universal Modular Actor Formalism for Artificial Intelligence“
1973 - Feiman, J.
„The Gartner Programming Language Survey (October 2001)“
Gartner Advisory - Harold Abelson, Gerald Jay Sussman, Julie Sussman:
Structure and Interpretation of Computer Programs
MIT Press. 1985, 1996 (a možná vyšel i další přetisk) - Paul Graham
On Lisp
Prentice Hall, 1993
Dostupné online na 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. Odkazy na Internetu
- Common Lisp
https://lisp-lang.org/ - Why You Should Learn Lisp In 2022?
https://www.youtube.com/watch?v=GWdf1flcLoM - LOOP Common Lisps Superior For
https://www.youtube.com/watch?v=i4tmF_1nZng - Lisp VS C benchmarks
https://programming-language-benchmarks.vercel.app/lisp-vs-c - Common Lisp: An elegant design pattern
https://www.youtube.com/watch?v=9597LFlvMuE - Common Lisp Macros By Example Tutorial
https://lisp-journey.gitlab.io/blog/common-lisp-macros-by-example-tutorial/ - The Common Lisp Cookbook
https://lispcookbook.github.io/cl-cookbook/ - The Evolution of Lisp
https://www.csee.umbc.edu/courses/331/resources/papers/Evolution-of-Lisp.pdf - Awesome CL
https://github.com/CodyReichert/awesome-cl - LISP
https://taoofmac.com/space/dev/lisp - Repositář projektu femtolisp
https://github.com/JeffBezanson/femtolisp - Femtolisp – lightweight, robust lisp interpreter built on reusable C libraries
https://www.findbestopensource.com/product/femtolisp - YCombinator: Femtolisp: A lightweight, robust, scheme-like Lisp implementation
https://news.ycombinator.com/item?id=22094722 - Learning Julia by Anshul Joshi, Rahul Lakhanpal: Femtolisp
https://www.oreilly.com/library/view/learning-julia/9781785883279/2e85442f-d100–4b53-b8f7–7d20d62f0255.xhtml - The role of femtolisp in Julia?
https://discourse.julialang.org/t/the-role-of-femtolisp-in-julia/1902 - LispSyntax.jl: A clojure-like lisp syntax for julia
https://github.com/swadey/LispSyntax.jl - 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 - Interlisp.org: Dedicated to Restoring and Preserving the Interlisp experience
https://github.com/Interlisp - Warren Teitelman
https://en.wikipedia.org/wiki/Warren_Teitelman - InterLISP/65
http://www.atarimania.com/utility-atari-400–800-xl-xe-interlisp-65_12477.html - Lisp Editing in the 80s – Interlisp SEdit (Video)
https://www.youtube.com/watch?v=2qsmF8HHskg - Inter-LISP
http://www.atarimania.com/utility-atari-400–800-xl-xe-inter-lisp_29354.html - InterLISP 65 Editing (video)
https://www.youtube.com/watch?v=nY_hcazo86A - Datasoft INTER-LISP/65 (Atari Age, chat)
https://atariage.com/forums/topic/116093-datasoft-inter-lisp65/ - Marvin Minsky – The beauty of the Lisp language (44/151)
https://www.youtube.com/watch?v=YaWVHyIBVeI - History of LISP (Interlisp)
http://www.softwarepreservation.org/projects/LISP/index.html#INTERLISP_ - Computer-Assisted Instruction (Bits and Bytes, Episode 7)
https://www.youtube.com/watch?v=eURtTV_qKw8 - 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/ - 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 - 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 - LISP: Lex Fridman's favorite programming language
https://www.youtube.com/watch?v=cMMiaCtOzV0 - What is the Curse of Lisp?
https://www.youtube.com/watch?v=_J3×5yvQ8yc - Array Programming Re-Imagined in Lisp
https://github.com/phantomics/april - What is Nil Punning?
https://www.youtube.com/watch?v=xiYKuDk6G-o - Python VS Common Lisp, workflow and ecosystem
https://lisp-journey.gitlab.io/pythonvslisp/ - A fast-moving Common Lisp software distribution
https://ultralisp.org/ - Numcl
https://github.com/numcl/numcl - Petalisp
https://github.com/marcoheisig/Petalisp - Common Lisp for the Curious Clojurian – Alan Dipert – Scicloj meeting 19
https://www.youtube.com/watch?v=44Q9ew9JH_U - Peter Norvig on Python
https://serverhorror.wordpress.com/2010/10/19/peter-norvig-on-python/ - A History of the Common Lisp
https://www.cleverism.com/skills-and-tools/common-lisp/ - Starting with Common Lisp in 2020
http://dnaeon.github.io/starting-with-common-lisp-in-2020/ - JACL: JavaScript Assisted Common Lisp
https://tailrecursion.com/JACL/