Common Lisp: žralok mezi programovacími jazyky (2.část)

28. 4. 2022
Doba čtení: 55 minut

Sdílet

 Autor: Depositphotos
Ve druhé části miniseriálu o Common Lispu se seznámíme se základními stavebními prvky tohoto jazyka. Mezi tyto prvky patří především atomy, uspořádané páry, z nich odvozené seznamy, anonymní funkce i pojmenované funkce.

Obsah

1. Základní datové typy Lispu

2. Atomy

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

7. LISP = LISt Processor

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í

13. Anonymní funkce

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

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

19. Literatura

20. Odkazy na Internetu

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).

Poznámka: je dobré si uvědomit, že jak na atomy, tak i na uspořádané páry je vhodné se dívat jako na hodnoty, které jsou neměnné (immutable). Pokud se například v programu použije hodnota 42, nelze tuto hodnotu žádným způsobem změnit (ovšem lze z ní odvodit jinou hodnotu aplikací funkce). To zajisté není nijak překvapivé, ovšem totéž pravidlo platí (v klasických LISPech) i pro další typy atomů (včetně řetězců) a taktéž pro uspořádané páry. A vzhledem k tomu, že seznamy jsou tvořeny uspořádanými páry (jak uvidíme dále), je případná modifikace seznamu možná jen výměnou nějakého páru za pár jiný.

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

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).

Poznámka: jednou z poměrně zásadních odlišností mezi Scheme a LISPem je rozdílná reprezentace pravdivostních hodnot. Zatímco LISP považuje prázdný seznam (), reprezentovaný též hodnotou nil, za nepravdu a všechny ostatní hodnoty za pravdu (včetně vždy definovaného symbolu T, jak již dobře víme), existuje ve Scheme jen jediná globálně dostupná a v celém systému jedinečná nepravdivá hodnota označovaná symbolem #f. Všechny ostatní objekty, včetně prázdného seznamu, jsou považovány za hodnotu pravdivou, což může komplikovat převody zdrojových kódů programů, protože v poměrně velkém množství algoritmů se například zpracovávají seznamy takovým způsobem, že se z nich postupně odebírají prvky a algoritmus skončí v případě odebrání posledního prvku, neboť se prázdný seznam vyhodnotí na nepravdivou hodnotu. Odlišnosti nalezneme i v Clojure, které rozlišuje mezi nil/false a true, přičemž hodnoty true a false odpovídají primitivním hodnotám jazyka Java (které jsou již z principu jejich funkce jedináčci).

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)
Poznámka: levý prvek tohoto páru je tradičně označován zkratkou car a pravý prvek zkratkou cdr. Důvody budou uvedeny v následující kapitole.

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.

ibm07

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).

Poznámka: s velkou pravděpodobností se jednalo o první využití garbage collectoru v široce používaném obecném programovacím jazyce.

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
Poznámka: kdysi jsem četl pěkné porovnání uspořádaných dvojic s konceptem DNA. Tedy to, co je pro IT uspořádaná dvojice (zcela základní struktura, která umožňuje spojovat „atomy“ do větších struktur), tím je DNA (a RNA) v biologii.

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
Poznámka: takto přímočaře nelze reprezentovat jakýkoli graf. Například se jedná o graf s cyklem, řekněme cyklický buffer. Pro tyto účely se používá dvojice kroků – vytvoření acyklické struktury a přidání poslední vazby, která vytvoří cyklický graf:
(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ů:

  1. čí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)
  2. 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é)
  3. 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)
  4. 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))
Poznámka: matice a vektory budou popsány příště.

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).

Poznámka: může se to zdát podivné a možná i zvrácené, ale například původní FORTRAN a mnohem novější standardní BASIC funkce nepodporovaly. Do FORTRANu byly základní konstrukce strukturovaného programování přidány až v rámci FORTRANu II v roce 1958.

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é.

Poznámka: tento způsob – poslední výraz ve funkci je současně (po vyhodnocení) její návratovou hodnotou – se používá i v některých moderních programovacích jazycích, které tak mnohdy nevyžadují explicitní použití klíčového slova return.

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))
Poznámka: proměnné parametry se tedy vždy uloží do seznamu, který může být i prázdný, neboli nil.

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
Poznámka: ve Scheme/Racketu se používá tento zápis:
(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"))
Poznámka: ve Scheme/Racketu se používá tento zápis:
#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.

Poznámka: s podobným konceptem se můžeme setkat například v programovacím jazyku Python, viz například Keyword (Named) Arguments in Python: How to Use Them.

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)
Poznámka: ve Scheme/Racketu se používá tento zápis:
#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:

# 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
       
36 predicate_atom.lisp predikát atom https://github.com/tisnik/lisp-families/blob/master/common-lisp/predicate_atom.lisp
37 function_optional_params1.lisp funkce s volitelnými parametry https://github.com/tisnik/lisp-families/blob/master/common-lisp/function_optional_params1.lisp
38 function_optional_params2.lisp funkce s volitelnými parametry https://github.com/tisnik/lisp-families/blob/master/common-lisp/function_optional_params2.lisp
39 function_optional_rest_params1.lisp funkce s volitelnými parametry i proměnným počtem parametrů https://github.com/tisnik/lisp-families/blob/master/common-lisp/function_optional_res­t_params1.lisp
40 function_optional_rest_params2.lisp funkce s volitelnými parametry i proměnným počtem parametrů https://github.com/tisnik/lisp-families/blob/master/common-lisp/function_optional_res­t_params2.lisp
41 function_optional_rest_params3.lisp nekorektní použití https://github.com/tisnik/lisp-families/blob/master/common-lisp/function_optional_res­t_params2.lisp
42 function_rest_params1.lisp funkce s proměnným počtem parametrů https://github.com/tisnik/lisp-families/blob/master/common-lisp/function_rest_params1.lisp
43 function_rest_params2.lisp funkce s proměnným počtem parametrů https://github.com/tisnik/lisp-families/blob/master/common-lisp/function_rest_params2.lisp
44 function_rest_params3.lisp funkce s proměnným počtem parametrů https://github.com/tisnik/lisp-families/blob/master/common-lisp/function_rest_params3.lisp
45 function_keyword_params1.lisp funkce s keyword parametry https://github.com/tisnik/lisp-families/blob/master/common-lisp/function_keyword_params1.lisp
46 function_keyword_params2.lisp funkce s keyword parametry https://github.com/tisnik/lisp-families/blob/master/common-lisp/function_keyword_params2.lisp
47 function_optional_keyword_params.lisp funkce s keyword parametry i volitelnými parametry https://github.com/tisnik/lisp-families/blob/master/common-lisp/function_optional_ke­yword_params.lisp
48 function_return.lisp předčasný výskok z funkce https://github.com/tisnik/lisp-families/blob/master/common-lisp/function_return.lisp
49 lambdas1.lisp lambda výrazy v LISPu https://github.com/tisnik/lisp-families/blob/master/common-lisp/lambdas1.lisp
50 lambdas2.lisp lambda výrazy v LISPu https://github.com/tisnik/lisp-families/blob/master/common-lisp/lambdas2.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/
  19. Common Lisp: žralok mezi programovacími jazyky
    https://www.root.cz/clanky/common-lisp-zralok-mezi-programovacimi-jazyky/

Č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.