Základní knihovna jazyka Racket

10. 9. 2019
Doba čtení: 37 minut

Sdílet

Ve třetím článku o jazyce Racket si popíšeme některé důležité základní knihovny, které poskytují mnoho užitečných funkcí – od zpracování seznamů přes práci s vektory a hešovacími mapami až po zpracování obecných proudů (streams).

Obsah

1. Seznamy v roli základního strukturovaného datového typu všech LISPovských jazyků

2. Základní funkce pro zpracování seznamů

3. Průchod všemi prvky seznamu s využitím funkce for/list

4. Funkce vyššího řádu map

5. Další varianty funkce map

6. Funkce vyššího řádu filter

7. Funkce vyššího řádu foldl a foldr

8. Vektory

9. Funkce a makra používaná pro zpracování vektorů

10. Hešovací tabulky

11. Funkce a makra používaná pro práci s hešovacími tabulkami

12. Generátor číselné řady (typu range)

13. Specializované datové typy

14. Celočíselné numerické hodnoty s pevným počtem bitů (fixnum)

15. Typ fxvector

16. Typ flvector

17. Typ stream

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

19. Literatura

20. Odkazy na Internetu

1. Seznamy v roli základního strukturovaného datového typu všech LISPovských jazyků

Nejdříve se zaměříme na funkce a makra používaná pro zpracování různých datových struktur podporovaných programovacím jazykem Racket. Podobně, jako je tomu v prakticky jakémkoli jiném lispovském programovacím jazyku, je základním strukturovaným datovým typem seznam (list), na který se taktéž můžeme dívat jako na vzájemně provázané tečka-dvojice (ostatně samotný název LIST vznikl z dvousloví „LISt Processor“). Nejprve si připomeňme, jakým způsobem je vlastně možné seznam vytvořit. V případě, že máme k dispozici konkrétní prvky, které se mají do seznamu vložit, použijeme konstruktor list, popř. můžeme seznam reprezentovat přímo zápisem jeho prvků do závorek.

Poznámka: ve všech demonstračních příkladech se bude dodržovat následující konvence – tučným písmem je označen text zapisovaný vývojářem, normálním písmem text vyhodnocovaný interpretrem programovacího jazyka Racket a kurzívou případné poznámky uvozované středníkem.

Prázdný seznam:

(list)
'()
 
empty
'()

Seznam celých čísel:

(list 1 2 3 4)
'(1 2 3 4)

Seznam řetězců:

(list "www" "root" "cz")
'("www" "root" "cz")

Seznam symbolů:

(list '#:foo '#:bar '#:baz)
'(#:foo #:bar #:baz)

Vnořený seznam:

(list (list 1 2) (list 3 4) (list 5 (list 6)))
'((1 2) (3 4) (5 (6)))

Seznam lze napsat i přímo ve formě prvků umístěných do závorek, ovšem nesmíme zapomenout na použití speciální formy quote, aby se interpret nesnažil o vyhodnocení seznamu (jako funkce):

(quote (1 2 3 4))
(quote "www" "root" "cz")

Speciální forma quote se v praxi používá tak často, že je umožněno ji zkrátit a zapisovat pomocí apostrofu:

'(1 2 3 4)
'(1 2 3 4)
 
'("www" "root" "cz")
'("www" "root" "cz")
Poznámka: v praxi se prakticky nikdy nesetkáme s tím, že by vývojáři formu quote takto rozepisovali; téměř vždy se používá apostrof. Ovšem například při výpisu expandovaného makra (viz předchozí část tohoto seriálu) se quote objevit může.

Jak se tedy liší použití konstruktoru list od přímého zapsání seznamu s quote? V prvním případě mohou být prvky seznamy vyhodnoceny (vypočítány), ve druhém případě nikoli:

(list (+ 1 2) (+ 3 4))
'(3 7)
 
(quote ((+ 1 2) (+ 3 4)))
'((+ 1 2) (+ 3 4))

2. Základní funkce pro zpracování seznamů

V knihovnách programovacího jazyka Racket je k dispozici poměrně velké množství funkcí určených pro práci se seznamy. Mnohé z těchto funkcí bylo převzato ze starších dialektů jazyka LISP, popř. přímo z programovacího jazyka Scheme, další funkce pak nalezneme v balíčku racket/list. Některé z těchto funkcí si ukážeme na (velmi jednoduchých) demonstračních příkladech.

Zjištění délky seznamu, tj. počtu prvků v seznamu:

(define x '(1 2 3))
(length x)
3
 
(define z '())
(length z)
0

Výběr n-tého prvku ze seznamu (i s případnou chybou, pokud index nespadá do očekávaného rozsahu):

(list-ref x 2)
3
 
(list-ref x 4)
; list-ref: index too large for list
;   index: 4
;   in: '(1 2 3)
; [,bt for context]

Výběr prvního prvku ze seznamu:

(first x)
1
Poznámka: v tradičním LISPu je tato funkce pojmenována car. Nic nám ovšem nebrání použít car i v Racketu:
(car x)
1

Vrácení zbytku seznamu bez prvního prvku:

(rest x)
'(2 3)
Poznámka: opět se vraťme k tradičním LISPům, kde se tato funkce jmenuje cdr. I tuto funkci v Racketu nalezneme:
(cdr x)
'(2 3)

Test, zda seznam obsahuje daný prvek či nikoli:

(member 2 x)
'(2 3)
 
(member 3 x)
'(3)
 
(member 1 x)
'(1 2 3)
 
(member 4 x)
#f
Poznámka: v tomto případě se vrací pravdivostní hodnota #f nebo jiná hodnota vyhodnocovaná jako pravda (tato hodnota je současně obsahem seznamu za nalezeným prvkem, což se může hodit).

Otočení prvků v seznamu:

(reverse x)
'(3 2 1)

Vložení nového prvku do seznamu na jeho začátek (což je jediná operace, kterou lze provést v konstantním čase):

(cons 4 x)
'(4 1 2 3)
 
(cons 4 z)
'(4)

Vytvoření seznamů z tečka dvojic:

(cons 1 empty)
'(1)
 
(cons 1 (cons 2 empty))
'(1 2)
 
(cons 1 (cons 2 (cons 3 empty)))
'(1 2 3)

Chování funkce cons se odlišuje podle toho, jakého typu jsou parametry této funkci předané. Běžného chování známého z jiných jazyků dosáhneme tehdy, pokud funkci předáme dva atomy. Výsledkem v tomto případě bude tečka-dvojice:

(cons 1 2)
'(1 . 2)
 
(cons 1 (cons 2 3))
'(1 2 . 3)
 
(cons (cons 1 2) (cons 3 4))
'((1 . 2) 3 . 4)

Spojení dvou či více seznamů:

(append x x)
'(1 2 3 1 2 3)
 
(append x x (list 9 9 9))
'(1 2 3 1 2 3 9 9 9)
 
(append '(0 0 0) x x (list 9 9 9))
'(0 0 0 1 2 3 1 2 3 9 9 9)
Poznámka: povšimněte si, že v programovacím jazyku Racket používáme identifikátor empty pro označení prázdného seznamu. Alternativně lze použít i zápis '(), který je používán v dalších dialektech LISPu či Scheme.

K dipozici je i predikát pro zjištění, zda je zkoumaná hodnota seznamem či nikoli:

(define x '(1 2 3))
(list? x)
#t
 
(define y 123)
(list? y)
#f
 
(define z '())
(list? z)
#t
 

3. Průchod všemi prvky seznamu s využitím funkce for/list

Pro průchod všemi prvky seznamu můžeme použít funkci nazvanou příznačně for/list (připomeňme si, že v Racketu nepatří lomítko mezi specializované znaky a tudíž se může objevit i ve jméně funkce). Použití je triviální:

(for/list ((item (list 1 2 3 4 5))) (+ item item))
'(2 4 6 8 10)

Rozepsání na více řádků a použití znaků [] namísto () pro větší čitelnost:

(for/list ([item (list 1 2 3 4 5)])
          (+ item item))
'(2 4 6 8 10)

Průchod dvěma seznamy:

(for/list ([i1 (list 1 2 3 4)] [i2 (list 6 7 8 9)]) (+ i1 i2))
'(7 9 11 13)

Pokud je jeden ze seznamů kratší, zkrátí se i počet průchodů smyčkou:

(for/list ([i1 (list 1 2 3 4)] [i2 (list 6 7 8 9 10)]) (+ i1 i2))
'(7 9 11 13)
 
(for/list ([i1 (list 1 2 3 4 5)] [i2 (list 6 7 8 9)]) (+ i1 i2))
'(7 9 11 13)

V navazující kapitole popsanou funkci map je možné naprogramovat právě s využitím for/list, a to například následujícím způsobem:

(define (map-function fce lst)
  (for/list ([item lst])
    (fce item)))

4. Funkce vyššího řádu map

Zatímco v běžných imperativních programovacích jazycích se seznamy zpracovávají prvek po prvku s využitím nějaké formy programové smyčky, v jazyku Racket se setkáme spíše s použitím několika funkcí vyššího řádu, které jako svůj vstup akceptují seznam a nějakou funkci, která je postupně aplikována buď na prvky seznamu, nebo na prvek seznamu a akumulátor, jehož hodnota se postupně při zpracovávání jednotlivých prvků seznamu mění. Výsledkem bývá buď nový seznam, nebo výsledná hodnota akumulátoru. Tyto funkce se většinou nazývají map, filter a reduce či fold a v Racketu existuje hned několik variant od každé této funkce.

První varianta funkce map se jmenuje … map. Tato funkce vyššího řádu akceptuje (v prvním parametru) funkci s jedním vstupním parametrem, která bude aplikována na prvky seznamu, který musí být do map předán jako druhý parametr. Pochopitelně se podíváme na demonstrační příklady, protože se s funkcí map v praxi setkáme velmi často.

Poznámka: funkce map, resp. přesněji řečeno koncept, na němž je postavena, se pravděpodobně poprvé objevil právě v lispovských programovacích jazycích a odtud byl později přenesen i do dalších funkcionálních jazyků a ještě později i do běžných (řekněme mainstreamových) jazyků.

Aplikace existující funkce na seznam, výsledkem bude jiný seznam:

(map sqrt (list 1 4 9 16 25))
'(1 2 3 4 5)

Výsledný seznam může obsahovat i prvky odlišného typu:

(map even? (list 1 4 9 16 25))
'(#f #t #f #t #f)
 
(map string-length (list "www" "." "root" "." "cz"))
'(3 1 4 1 2)

V případě, že budeme chtít na prvky seznamu aplikovat vlastní funkci (s jedním vstupem), máme dvě možnosti:

  1. Vytvořit pojmenovanou funkci a tu následně do map předat
  2. Přímo ve funkci map deklarovat funkci anonymní, která bude omezena právě na ono jediné volání funkce map

Opět si pochopitelně oba dva zmíněné případy ukážeme prakticky. Budeme chtít, aby se hodnoty všech prvků seznamu zdvojnásobily. Pochopitelně je snadné si vytvořit novou funkci pojmenovanou například double a tu následně pro tento účel použít:

(define (double x) (+ x x))
(map double (list 1 2 3 4 5))
'(2 4 6 8 10)

Alternativně můžeme podobnou funkcionalitu implementovat jako funkci anonymní na jediném řádku:

(map (lambda (x) (+ x x)) (list 1 2 3 4 5))
'(2 4 6 8 10)

Vzhledem k tomu, že výsledkem funkce map je opět seznam, lze volání vnořit, i když to nemusí být příliš čitelné:

(map (lambda (x) (/ 1 x))
     (map (lambda (x) (+ x x))
          (list 1 2 3 4 5)))
'(1/2 1/4 1/6 1/8 1/10)

Nebo:

(define (inc x) (+ 1 x))
(inc 3)
4
 
(map inc
     (map (lambda (x) (/ 1 x))
          (map (lambda (x) (+ x x))
               (list 1 2 3 4 5))))
'(3/2 5/4 7/6 9/8 11/10)

5. Další varianty funkce map

Ve skutečnosti existují v programovacím jazyce Racket, resp. přesněji řečeno v jeho základních knihovnách, i další varianty funkce map. Poměrně často potřebujeme zjistit, zda prvky seznamu odpovídají nějaké podmínce. Podmínku lze zapsat s využitím klasického predikátu, tj. funkce s jedním vstupním parametrem, která vrací pravdivostní hodnotu #t nebo #f. Pokud použijeme funkci vyššího řádu map s takovým predikátem, bude výsledkem nový seznam obsahující pouze pravdivostní hodnoty.

Pokud například budeme chtít zjistit, které prvky jsou sudé, můžeme postupovat takto:

(map even? (list 1 4 9 16 25))
'(#f #t #f #t #f)

Často však potřebujeme zjistit, jestli všechny prvky odpovídají zadané podmínce (predikátu), zda žádný prvek predikátu neodpovídá, nebo dokonce zda alespoň nějaký (tedy minimálně jeden) prvek predikátu odpovídá. Budeme tedy chtít výsledný vektor nějakým způsobem konsolidovat do jediné pravdivostní hodnoty #t nebo #f. A právě pro tento účel slouží další varianty funkce map nazvané příznačně andmap a ormap. Na následujících řádcích je ukázáno, jak se tyto varianty chovají při zpracování vstupních seznamů.

Je alespoň jeden prvek sudý?

(ormap even? (list 1 4 9 16 25))
#t
 
(ormap even? (list 1 3 5))
#f

Jsou všechny prvky seznamu sudé?

(andmap even? (list 1 4 9 16 25))
#f
 
(andmap even? (list 2 4 6))
#t

Chování u prázdného seznamu může být poněkud překvapující, ovšem je konzistentní se sémantikou prováděných operací:

(map even? empty)
'()
 
(andmap even? empty)
#t
 
(ormap even? empty)
#f
Poznámka: ve skutečnosti je chování funkcí andmap a ormap do značné míry podobné dále popsané funkci foldl v tom ohledu, že je seznam zpracován a „akumulován“ do jediné výsledné hodnoty.

6. Funkce vyššího řádu filter

Funkci map zmíněnou v předchozích kapitolách pravděpodobně mnoho čtenářů již zná z dalších programovacích jazyků. Týká se to i funkce nazvané filter, která dnes již patří k základní nabídce funkcí i v mainstreamových programovacích jazycích (kde bývá zobecněna pro zpracování libovolných streamů či sekvencí). I tato funkce je funkcí vyššího řádu, a to z toho důvodu, že jako svůj vstup akceptuje jinou funkci. Zde se konkrétně musí jednat o predikát, tedy o funkci vracející pro každý svůj vstup pravdivostní hodnotu #t nebo #f. Na základě výsledné hodnoty predikátu se právě zpracovávaný prvek buď objeví ve výstupním seznamu či nikoli – tato funkce tedy obecně vrací seznam, ale s odlišným počtem prvků, než kolik jich má seznam vstupní (pokud není predikát splněn pro žádný prvek ze vstupního seznamu, bude výsledkem prázdný seznam).

Opět se podívejme na několik demonstračních příkladů (s několika jsme se již setkali v předchozích částech tohoto seriálu při popisu ostatních dialektů programovacího jazyka Scheme):

(filter even? (list 1 2 3 4 5 6 7))
'(2 4 6)
 
(filter odd? (list 1 2 3 4 5 6 7))
'(1 3 5 7)
 
(filter positive? (list 1 2 3 4 5 6 7))
'(1 2 3 4 5 6 7)
 
(filter negative? (list 1 2 3 4 5 6 7))
'()

Můžeme si samozřejmě vytvořit i vlastní predikát a ten následně použít:

(define (not-dot s) (not (eq? s ".")))
 
(filter not-dot (list "www" "." "root" "." "cz"))
'("www" "root" "cz")

Použití anonymní funkce:

(filter (lambda (s) (not (eq? s "."))) (list "www" "." "root" "." "cz"))
'("www" "root" "cz")

Čitelnější varianta:

(filter (lambda (s) (not (eq? s ".")))
        (list "www" "." "root" "." "cz"))
'("www" "root" "cz")

7. Funkce vyššího řádu foldl a foldr

Ve většině programovacích jazyků inspirovaných funkcionálním programováním se kromě funkcí typu map a filter setkáme i s funkcí vyššího řádu, která se nazývá buď reduce nebo fold. Programovací jazyk Racket pochopitelně není výjimkou, takže i v něm nalezneme tento typ funkce, a to dokonce v několika variantách. Základní funkcí tohoto typu je funkce nazvaná foldl, která postupně zpracovává všechny prvky seznamu zleva doprava a aplikuje na každý prvek a akumulovanou hodnotu nějakou funkci. Výsledkem je v každé iteraci nová hodnota akumulátoru a po projití celého seznamu je výsledná hodnota uložená v akumulátoru současně i návratovou hodnotou funkce foldl. Samotné volání této funkce je ovšem nepatrně složitější, než tomu bylo u map a filter. Je tomu tak z toho důvodu, že kromě zpracovávající funkce (se dvěma parametry) a vstupního seznamu musíme funkci foldl předat i počáteční hodnotu akumulátoru.

Opět si ukážeme několik demonstračních příkladů.

Součet hodnot v seznamu a součet s počáteční hodnotou akumulátoru nastavenou na 100:

(foldl + 0 (list 1 2 3 4 5))
15
 
(foldl + 100 (list 1 2 3 4 5))
115

Postupná konstrukce seznamu z tečka-dvojic:

(foldl cons empty (list 1 2 3 4 5))
'(5 4 3 2 1)

Použití funkce foldr, která seznam zpracovává od posledního prvku:

(foldl cons empty (list 1 2 3 4 5))
'(1 2 3 4 5)

Při některých výpočtech je výsledek volání foldl a foldr totožný, ovšem foldl je paměťově efektivnější:

(foldr + 0 (list 1 2 3 4 5))
15
 
(foldr + 100 (list 1 2 3 4 5))
115

8. Vektory

Výše popsané seznamy jsou v lispovských programovacích jazycích základním strukturovaným datovým typem. Interně se jedná, jak již víme z předchozích částí tohoto seriálu, o lineárně vázané prvky, přičemž každý prvek je představován tečka dvojicí. V první části tečka-dvojice je uložena hodnota prvku, v části druhé pak reference na další prvek. Toto uspořádání je sice flexibilní a umožňuje snadnou tvorbu nového seznamu ze seznamu původního (cons, append), ovšem nevýhodou je pomalý přístup k jednotlivým prvkům, který je proveden v lineárním čase; tj. časová složitost je O(n). V případě, že je zapotřebí zajistit konstantní přístup k jednotlivým prvkům na základě jejich indexu, budeme s velkou pravděpodobností preferovat strukturovaný datový typ, který se bude chovat podobně jako pole v jiných programovacích jazycích. Takovým datovým typem je v jazyku Racket typ nazvaný příznačně vektor (vector).

Literál vektoru se zapisuje:

#(jednotlivé prvky vektoru)

Příklad:

#(1 2 3 4)
'#(1 2 3 4)
 
#()
'#()

Konstruktorem vektorů je funkce vektor:

(vector 1 2 3 4)
'#(1 2 3 4)
 
(vector)
'#()

Vektor může obsahovat prvky libovolného typu:

(vector (list 1 2 3 4) (vector 1 2 3 4))
'#((1 2 3 4) #(1 2 3 4))
 
(vector "foo" (list 1 2 3 4) "bar" (vector 1 2 3 4) "baz")
'#("foo" (1 2 3 4) "bar" #(1 2 3 4) "baz")

Takto vytvořený vektor je měnitelný (mutable).

Vytvořit můžeme i vektor o zadané kapacitě prvků:

(make-vector 10)
'#(0 0 0 0 0 0 0 0 0 0)

Takový vektor lze i inicializovat:

(make-vector 10 1/2)
'#(1/2 1/2 1/2 1/2 1/2 1/2 1/2 1/2 1/2 1/2)

K dispozici je i predikát testující, zda je hodnota typu vektor či nikoli:

(vector? x)
#f
 
(vector? y)
#t
 
(vector? (vector))
#t
Poznámka: povšimněte si rozdílu oproti programovacímu jazyku Clojure, v němž se prvky vektoru zapisují do hranatých závorek. Interně jsou ovšem vektory v Clojure reprezentovány odlišným a nepatrně složitějším způsobem jako „široké“ stromy. V případě programovacího jazyka Racket se ovšem jedná o skutečnou obdobu polí. Navíc se v Racketu mohou hranaté závorky používat ve stejném významu jako závorky kulaté, takže někteří autoři používají zápis, který do značné míry připomíná jazyk Logo.

9. Funkce a makra používaná pro zpracování vektorů

V základní knihovně programovacího jazyka Racket je k dispozici několik desítek funkcí určených pro práci s vektory. Další funkce nalezneme v rozšiřující knihovně nazvané racket/vector, kterou je ovšem ve vytvářených skriptech potřeba explicitně načíst příkazem:

(require racket/vector)

Použití těchto funkcí si opět ukážeme na příkladech.

Vektory, s nimiž budeme pracovat:

(define v1 (vector 1 2 3 4))
(define v3 (vector))
(vector-length v3)

Délka vektorů (počet prvků):

(vector-length v1)
4
 
(vector-length v2)
4
 
(vector-length v3)
0

Přístup k prvkům vektorů (čtení) s případným nahlášením chyb:

(vector-ref v1 0)
1
 
(vector-ref v1 2)
3
 
(vector-ref v1 10)
; vector-ref: index is out of range
;   index: 10
;   valid range: [0, 3]
;   vector: '#(1 2 3 4)
; [,bt for context]

Nastavení nové hodnoty měnitelného vektoru, opět s případným hlášením chyby:

(vector-set! v1 2 -5)
 
v1
'#(1 2 -5 4)
 
(vector-set! v1 10 -5)
; vector-set!: index is out of range
;   index: 10
;   valid range: [0, 3]
;   vector: '#(1 2 -5 4)
; [,bt for context]

Vyplnění všech prvků vektoru stejnou hodnotou:

(vector-fill! v1 0)
 
v1
'#(0 0 0 0)

Převod vektoru na seznam:

(vector->list v1)
'(0 0 0 0)
 
(vector->list v2)
'(4 5 6 7)
 
(vector->list v3)
'()

Převod seznamu na vektor:

(list->vector '())
'#()
 
(list->vector '(1))
'#(1)
 
(list->vector '(1 2 3))
'#(1 2 3)

Procházení prvky vektoru:

(for/vector ([i v1]) (fx* i i))
'#(1 25 9 16 25)

10. Hešovací tabulky

Dalším velmi užitečným a často používaným strukturovaným datovým typem v programovacím jazyku Racket jsou hešovací tabulky (hash table). Podobně jako u seznamů a vektorů, je možné i u hešovacích tabulek zvolit, zda se má jednat o neměnnou hodnotu či o hodnotu, kterou lze za běhu aplikace modifikovat. Neměnná (immutable) hešovací tabulka se vytvoří pomocí konstruktoru hash, kterému se předá libovolný počet dvojic klíč-hodnota:

(hash "red" "#ff0000" "green" "#00ff00" "blue" "#0000ff")
'#hash(("blue" . "#0000ff") ("green" . "#00ff00") ("red" . "#ff0000"))

Při porovnávání klíčů prvků vkládaných do hešovací tabulky se používají predikáty equal?, eq? nebo eqv? (eq? provádí nejsilnější porovnání, porovnání je současně nejrychlejší). Výše uvedený konstruktor hash používá predikát equal?, ovšem pochopitelně můžeme zvolit i další dva predikáty. Využívá se přitom odlišných konstruktorů hasheq a hasheqv:

(hasheq "red" "#ff0000" "green" "#00ff00" "blue" "#0000ff")
'#hasheq(("blue" . "#0000ff") ("green" . "#00ff00") ("red" . "#ff0000"))
 
(hasheqv "red" "#ff0000" "green" "#00ff00" "blue" "#0000ff")
'#hasheqv(("blue" . "#0000ff") ("green" . "#00ff00") ("red" . "#ff0000"))

Měnitelné hešovací tabulky, do nichž je možné v čase přidávat další prvky, se vytváří s využitím konstruktoru make-hash, popř. jeho alternativ make-hasheq a make-hasheqv:

(define h1 (make-hash))
 
(define h2 (make-hasheq))
 
(define h3 (make-hasheqv))
 
h1
'#hash()
 
h2
'#hasheq()
 
h3
'#hasheqv()

Prvky měnitelné hešovací tabulky lze inicializovat, a to předáním seznamu obsahujícího dvojice klíč-hodnota (každá dvojice by měla být podseznamem):

(define h1 (make-hash '(["red" "#ff0000"] ["green" "#00ff00"] ["blue" "#0000ff"])))
 
(define h2 (make-hasheq '(["red" "#ff0000"] ["green" "#00ff00"] ["blue" "#0000ff"])))
 
(define h3 (make-hasheqv '(["red" "#ff0000"] ["green" "#00ff00"] ["blue" "#0000f
 
h1
'#hash(("blue" . ("#0000ff")) ("green" . ("#00ff00")) ("red" . ("#ff0000")))
 
h2
'#hasheq(("blue" . ("#0000ff")) ("green" . ("#00ff00")) ("red" . ("#ff0000")))
 
h3
'#hasheqv(("blue" . ("#0000ff")) ("green" . ("#00ff00")) ("red" . ("#ff0000")))
Poznámka: opět platí, že hranaté závorky [] jsme použili pouze pro větší čitelnost, ovšem stejně dobře lze použít ekvivalentní zápis s kulatými závorkami () nebo s libovolným mixem závorek (ovšem párové závorky musí být stejného typu).

Vzhledem k tomu, že hodnota prvku je získána operací cdr (což v našem případě vedlo k vytvoření hodnot tvořených jednoprvkovými seznamy), může být výhodnější při konstrukci hešovací tabulky použít přímo tečka-dvojice:

(make-hash '(["red" . "#ff0000"] ["green" . "#00ff00"] ["blue" . "#0000ff"]))
'#hash(("blue" . "#0000ff") ("green" . "#00ff00") ("red" . "#ff0000"))

11. Funkce a makra používaná pro práci s hešovacími tabulkami

Přečtení prvku uloženého pod známým klíčem:

(hash-ref h1 "blue")
"#0000ff"

Pokus o přečtení neexistujícího prvku:

(hash-ref h1 "foobar")
; hash-ref: no value found for key
;   key: "foobar"
; [,bt for context]

Specifikovat lze i hodnotu, která se vrací ve chvíli, kdy prvek nebyl nalezen:

(hash-ref h1 "blue" "nic")
"#0000ff"
 
(hash-ref h1 "foobar" "nic")
"nic"

Do měnitelných hešovacích tabulek se nová dvojice klíč-hodnota ukládá funkcí hash-set!:

(hash-set! h1 "red" "#ff0000")
 
(hash-set! h1 "green" "#00ff00")
 
(hash-set! h1 "blue" "#0000ff")
 
h1
'#hash(("blue" . "#0000ff") ("green" . "#00ff00") ("red" . "#ff0000"))

Přepis hodnoty prvku (se stejným klíčem):

(hash-set! h1 "blue" "rgb(0,0,1)")
 
h1
'#hash(("blue" . "rgb(0,0,1)") ("green" . "#00ff00") ("red" . "#ff0000"))

Odstranění prvku z hešovací tabulky:

(hash-remove! h1 "blue")
 
(hash-remove! h1 "foobar")
 
h1
'#hash(("green" . "#00ff00") ("red" . "#ff0000"))

U neměnitelných hešovacích tabulek se používají funkce hash-set a hash-remove (bez vykřičníku na konci), které ovšem pochopitelně nemodifikují původní tabulku, ale vrací tabulku novou:

(define h1 (hash "red" "#ff0000" "green" "#00ff00" "blue" "#0000ff"))
 
h1
'#hash(("blue" . "#0000ff") ("green" . "#00ff00") ("red" . "#ff0000"))
 
(hash-set h1 "white" "#ffffff")
'#hash(("blue" . "#0000ff")
       ("green" . "#00ff00")
       ("red" . "#ff0000")
       ("white" . "#ffffff"))
 
h1
'#hash(("blue" . "#0000ff") ("green" . "#00ff00") ("red" . "#ff0000"))

12. Generátor číselné řady (typu range)

V praxi se velmi často setkáme s požadavkem vytvoření sekvence čísel. Tento požadavek je v mnoha programovacích jazycích realizován nějakou základní funkcí; příkladem může být jazyk Python 2.x a jeho funkce range (v Pythonu 3 se sémantika této funkce změnila). V jazyku Racket se pro podobný účel používá funkce se jménem in-range, což je jméno, které začne dávat smysl ve chvíli, kdy ji použijeme společně se speciální formou for pro vytvoření programové smyčky. Tato funkce vrací takzvaný proud (stream):

(in-range 10)
#

Zkusme však tuto funkci zkombinovat s for/list. Využijeme přitom jak základní variantu funkce, v níž se zadává pouze koncová hodnota, tak i variantu s počáteční hodnotou a (nepovinným) krokem:

(for/list ([i (in-range 10)]) i)
'(0 1 2 3 4 5 6 7 8 9)
 
(for/list ([i (in-range 1 10)]) i)
'(1 2 3 4 5 6 7 8 9)
 
(for/list ([i (in-range 1 10 2)]) i)
'(1 3 5 7 9)
 
(for/list ([i (in-range 1 10 -2)]) i)
'()
 
(for/list ([i (in-range 10 0 -2)]) i)
'(10 8 6 4 2)

Použití pro zlomky (typ rational):

(for/list ([i (in-range 1 10 1/2)]) i)
'(1 3/2 2 5/2 3 7/2 4 9/2 5 11/2 6 13/2 7 15/2 8 17/2 9 19/2)

Použití pro reálná čísla (se známým problémem, který způsobuje hodnota 0,1):

(for/list ([i (in-range 0.0 1 0.1)]) i)
'(0.0
  0.1
  0.2
  0.30000000000000004
  0.4
  0.5
  0.6
  0.7
  0.7999999999999999
  0.8999999999999999
  0.9999999999999999)

Existuje ještě podobná funkce nazvaná in-naturals, která taktéž generuje číselnou řadu, ovšem v tomto případě nekonečnou (případná programová smyčka tedy nikdy neskončí, pokud v ní nepoužijeme nějakou jinou formu pro ukončení iteračního cyklu):

(for/list ([k (in-naturals)]
           [x (in-range 10)])
           (list k x))
'((0 0) (1 1) (2 2) (3 3) (4 4) (5 5) (6 6) (7 7) (8 8) (9 9))

popř.:

(for/list ([k (in-naturals)]
           [x (in-range 1 10)])
           (list k (/ 1 x)))
'((0 1) (1 1/2) (2 1/3) (3 1/4) (4 1/5) (5 1/6) (6 1/7) (7 1/8) (8 1/9))

13. Specializované datové typy

V navazujících kapitolách se ve stručnosti seznámíme s dalšími specializovanými datovými typy, které lze v programovacím jazyku Racket použít. V první řadě se jedná o numerický datový typ fixnum, dále o typ flonum, vektory s prvky fixnum a flonum a taktéž o zobecnění sekvencí, které je představováno datovým typem stream.

14. Celočíselné numerické hodnoty s pevným počtem bitů (fixnum)

Běžné aritmetické funkce typu + atd. dokážou pracovat s libovolným numerickým typem z numerické věže jazyka Racket. To je u vysokoúrovňového programovacího jazyka očekávatelné chování, ovšem zaplatíme za to delší dobou výpočtu a případně i větší spotřebou operační paměti. Pokud potřebujeme provádět rychlé výpočty jen s celými čísly (navíc shora omezenými), lze pro tento účel použít hodnoty typu fixnum, což jsou celá čísla (integer) o šířce 31 bitů na 32bitových systémech a 63 bitů na systémech 64bitových (jeden bit je rezervován pro tag).

Aby byly dále popsané funkce volatelné, je nutné nahrát příslušnou knihovnu:

(require racket/fixnum)

Poté lze namísto obecných funkcí +, -, < atd. použít funkci s prefixem „fx“:

(+ 1 2)
3
 
(fx+ 1 2)
3

Rozdíl mezi + a fx+ se projeví například ve chvíli, kdy překročíme rozsah 31/63 bitů:

(+ 1 4611686018427387903)
4611686018427387904
 
(fx+ 1 4611686018427387903)
; fx+: result is not a fixnum
;   result: 4611686018427387904
; [,bt for context]

Popř. se pokusíme o násobení velkých čísel, které může vést k přetečení (mezi)výsledku (ovšem bez nahlášení chyby):

(* (* 99999 99999) (* 99999 99999))
99996000059999600001
 
(fx* (fx* 99999 99999) (fx* 99999 99999))
-1461092345402933887

Pokud nám nevyhovuje kontrola na chyby přetečení, lze použít „nezabezpečené“ (unsafe) operace:

(require racket/unsafe/ops)
 
(unsafe-fx+ 1 4611686018427387903)
-4611686018427387904

Výpočet faktoriálu pouze nad typem fixnum s případným přetečením či dalšími problémy, které mohou nastat:

#lang racket/base
 
(require racket/fixnum)
 
(define (print item)
     (display item)
     (newline))
 
(define (factorial n)
    (let fact-iter (            ; pomocná vnitřní funkce
                  (n n)         ; počitadlo iterací
                  (result 1))   ; průběžný výsledek
        (if (fx= n 0)           ; po dosažení koncového stavu
            result              ; se vrátí průběžný výsledek
            (fact-iter (fx- n 1) (fx* n result)) ; koncové volání
        )))
 
(define n 0)
 
(do ()
  ((>= n 30))
  (print (factorial n))
  (set! n (+ n 1)))

S výsledky:

1
1
2
6
24
120
720
5040
40320
362880
3628800
39916800
479001600
6227020800
87178291200
1307674368000
20922789888000
355687428096000
6402373705728000
121645100408832000
2432902008176640000
-4249290049419214848
-1250660718674968576
fx*: result is not a fixnum
  result: -28658025453976518656
  context...:
   /home/tester/src/LISP/lisp-families/racket/base-libraries/05-factorial_fixnum.rkt:10:4: fact-iter
   /home/tester/src/LISP/lisp-families/racket/base-libraries/05-factorial_fixnum.rkt:20:0: doloop
   "/home/tester/src/LISP/lisp-families/racket/base-libraries/05-factorial_fixnum.rkt": [running body]
   temp37_0
   for-loop
   run-module-instance!125
   perform-require!78

15. Typ fxvector

Datový typ fxvektor odpovídá poli celých čísel z céčka či Javy. Je to tedy strukturovaný datový typ navržený a uložený takovým způsobem, aby umožnil rychlou práci s celými čísly i šířce 31, resp. 63 bitů, v ostatních ohledech se fxvektor chová prakticky stejně, jako klasický datový typ vector.

Vytvoření fxvektoru a dotaz, zda je nějaká hodnota tohoto typu:

(fxvector 1 2 3 4)
(fxvector 1 2 3 4)
 
(define v1 (fxvector 1 2 3 4 5))
 
(fxvector? v1)
#t

Základní operace s fxvektory jsou podobné, jako je tomu u běžných vektorů:

(fxvector-length v1)
5
 
(fxvector-ref v1 1)
2
 
(fxvector-ref v1 10)
; fxvector-ref: index is out of range
;   index: 10
;   valid range: [0, 4]
;   fxvector: (fxvector 1 2 3 4 5)
; [,bt for context]
 
(fxvector-set! v1 1 -5)
v1
(fxvector 1 -5 3 4 5)
 
(fxvector-set! v1 10 -5)
; fxvector-set!: index is out of range
;   index: 10
;   valid range: [0, 4]
;   fxvector: (fxvector 1 -5 3 4 5)
; [,bt for context]

Procházení všemi prvky fxvektoru:

(for/fxvector ([i v1]) i)
(fxvector 1 -5 3 4 5)
 
(for/fxvector ([i v1]) (fx* i i))
(fxvector 1 25 9 16 25)

16. Typ flvector

Dalším datovým typem, který se do značné míry podobá výše popsanému typu fxvector, je datový typ nazvaný flvector. Již název tohoto typu napovídá, jaké bude mít základní vlastnosti: jedná se o vektor obsahující prvky typu flonum, což je jen zvláštní název pro numerické hodnoty s plovoucí řádovou čárkou s dvojitou přesností podle IEEE 754. Jinými slovy se jedná o typ známý v jiných programovacích jazycích pod jménem double nebo float64.

Před použitím speciálních operací pro typ flonum je zapotřebí naimportovat příslušnou knihovnu:

(require racket/flonum)

Podobně jako u typu fixnum existovaly základní aritmetické a relační funkce s prefixem „fx“, jsou pro datový typ flonum definovány funkce, jejichž prefix je „fl“. Ukažme si několik jednoduchých příkladů.

Pokus o součet dvou hodnot funkcí fl+. Obě hodnoty musí být typu flonum:

(fl+ 10 20)
; fl+: contract violation
;   expected: flonum?
;   given: 10
;   argument position: 1st
; [,bt for context]
 
(fl+ 10. 20.)
30.0

Výpočet faktoriálu pouze s čísly typu flonum (což je obecně špatný nápad):

#lang racket/base
 
(require racket/flonum)
 
(define (print item)
     (display item)
     (newline))
 
(define (factorial n)
    (let fact-iter (              ; pomocná vnitřní funkce
                  (n n)           ; počitadlo iterací
                  (result 1.0))   ; průběžný výsledek
        (if (fl= n 0.0)           ; po dosažení koncového stavu
            result                ; se vrátí průběžný výsledek
            (fact-iter (fl- n 1.0) (fl* n result)) ; koncové volání
        )))
 
(define n 0.0)
 
(do ()
  ((>= n 30.0))
  (print (factorial n))
  (set! n (+ n 1)))

S výsledky:

1.0
1.0
2.0
6.0
24.0
120.0
720.0
5040.0
40320.0
362880.0
3628800.0
39916800.0
479001600.0
6227020800.0
87178291200.0
1307674368000.0
20922789888000.0
3.55687428096e+14
6.402373705728e+15
1.21645100408832e+17
2.43290200817664e+18
5.109094217170944e+19
1.1240007277776077e+21
2.585201673888498e+22
6.204484017332394e+23
1.5511210043330984e+25
4.032914611266057e+26
1.0888869450418352e+28
3.048883446117138e+29
8.841761993739701e+30

Deklarace vektoru typu flvector:

(flvector 1. 2. 3.)
(flvector 1.0 2.0 3.0)
 
(flvector 1. 2. 3)
; flvector: contract violation
;   expected: flonum?
;   given: 3
;   argument position: 3rd
; [,bt for context]

17. Typ stream

Posledním důležitým datovým typem, o němž se dnes alespoň ve stručnosti zmíníme, je typ nazvaný stream (proud). Na tento datový typ se můžeme dívat jako na zobecnění sekvencí. Nejdůležitějšími operacemi pro práci se streamem jsou operace představované funkcemi stream-first a stream-rest. Samotný stream je postupně vytvořen funkcí stream-cons, ovšem ve funkci streamu lze použít i běžné lineárně vázané seznamy (list). Čím se tedy vlastně streamy od seznamů odlišují? Seznam musí být konečný a všechny jeho prvky musí být vyhodnoceny, zatímco v případě streamů je možné mít nekonečnou sekvenci prvků a – což vyplývá z předchozího – nemusí být všechny prvky vyhodnoceny, ale jejich vyhodnocení se provádí až ve chvíli, kdy je to nezbytně nutné. Takové streamy nazýváme lazy streams, podobně jako v programovacím jazyce Clojure máme lazy sekvence (ostatně na lazy sekvencích je postavena prakticky celá základní knihovna Clojure).

Aby bylo možné tento datový typ použít, je nutné ve skriptu provést import příslušné knihovny:

(require racket/stream)

Ukažme si nyní (velmi jednoduché) použití streamů. Jak již víme z předchozího textu, lze stream postupně vytvořit funkcí stream-cons, nebo pomocí konstruktoru stream (jenž interně stream-cons volá):

(define s1 (stream 1 2 3 4 5))
 
s1
#<stream>
Poznámka: povšimněte si, že se stream skutečně nevyhodnocuje.

Funkce stream-first a stream-rest:

(stream-first s1)
1
(stream-rest s1)
#<stream>

Převod streamu na sekvenci:

(in-stream s1)
#<sequence>

Převod (konečného!) streamu na seznam:

(stream->list s1)
'(1 2 3 4 5)

Získání prvních tří prvků streamu:

ict ve školství 24

(stream-take s1 3)
#<stream>

Líná aplikace funkce na všechny prvky streamu:

(define (square x) (* x x))
 
(stream-map square s1)
#<stream>
 
(stream->list (stream-map square s1))
'(1 4 9 16 25)
Poznámka: výsledkem aplikace většiny funkcí na stream je jiný stream, což je užitečné při tvorbě takzvaných kolon.

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

Zdrojové kódy všech dnes použitých demonstračních příkladů byly uloženy do Git repositáře, který je dostupný na adrese https://github.com/tisnik/lisp-families.git (stále na GitHubu :-). V případě, že nebudete chtít klonovat celý repositář (ten je ovšem – alespoň prozatím – velmi malý, můžete namísto toho použít odkazy na jednotlivé příklady, které naleznete v následující tabulce:

# Příklad Popis příkladu Cesta
1 01-dot-pairs.rkt zpracování tečka-dvojic https://github.com/tisnik/lisp-families/blob/master/racket/base-libraries/01-dot-pairs.rkt
2 02-cons.rkt konstrukce tečka dvojic a seznamů https://github.com/tisnik/lisp-families/blob/master/racket/base-libraries/02-cons.rkt
3 03-lists_traditional.rkt zpracování seznamů, tradiční funkce https://github.com/tisnik/lisp-families/blob/master/racket/base-libraries/03-lists_traditional.rkt
4 04-lists_racket.rkt zpracování seznamů, funkce Racketu https://github.com/tisnik/lisp-families/blob/master/racket/base-libraries/04-lists_racket.rkt
5 05-factorial_fixnum.rkt výpočet faktoriálu nad typem fixnum https://github.com/tisnik/lisp-families/blob/master/racket/base-libraries/05-factorial_fixnum.rkt
6 06-factorial_flonum.rkt výpočet faktoriálu nad typem flonum https://github.com/tisnik/lisp-families/blob/master/racket/base-libraries/06-factorial_flonum.rkt
7 07-vectors.rkt základní operace s vektory https://github.com/tisnik/lisp-families/blob/master/racket/base-libraries/07-vectors.rkt
8 08-hash-map.rkt základní operace s hešovacími tabulkami https://github.com/tisnik/lisp-families/blob/master/racket/base-libraries/08-hash-map.rkt

19. Literatura

  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 stránce 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
  24. Graham Hutton
    A tutorial on the universality andexpressiveness of fold
    http://www.cs.nott.ac.uk/~pszgmh/fol­d.pdf

20. Odkazy na Internetu

  1. Racket: Macros
    https://www.it.uu.se/edu/cou­rse/homepage/avfunpro/ht13/lec­tures/Racket-3-Macros.pdf
  2. Beautiful Racket / explainers: Macros
    https://beautifulracket.com/ex­plainer/macros.html
  3. Macros (dokumentace k Racketu)
    https://docs.racket-lang.org/guide/macros.html
  4. Model syntaxe jazyka Racket
    https://docs.racket-lang.org/reference/syntax-model.html
  5. Syntax Objects
    https://docs.racket-lang.org/guide/stx-obj.html
  6. Tech behind Tech: Clojure Macros Simplified
    http://techbehindtech.com/2010/09/28/clo­jure-macros-simplified/
  7. Fatvat – Exploring functional programming: Clojure Macros
    http://www.fatvat.co.uk/2009/02/clo­jure-macros.html
  8. Beautiful Racket: an introduction to language-oriented programming using Racket
    https://beautifulracket.com/
  9. Stránky projektu Racket
    https://racket-lang.org/
  10. Dokumentace k projektu Racket
    https://docs.racket-lang.org/index.html
  11. Seznam dostupných balíčků pro Racket
    https://pkgs.racket-lang.org/
  12. Racket na Wikipedii
    https://en.wikipedia.org/wi­ki/Racket_(programming_lan­guage)
  13. Blogy o Racketu a navazujících technologiích
    https://blog.racket-lang.org/
  14. Prográmky psané v Racketu na RosettaCode
    http://rosettacode.org/wi­ki/Category:Racket
  15. Fear of Macros
    https://www.greghendershott.com/fear-of-macros/
  16. Rackjure
    https://github.com/greghen­dershott/rackjure
  17. 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/
  18. Racket News
    https://racket-news.com/
  19. Racket: Lisp for learning
    https://lwn.net/Articles/795385/
  20. Future of Racket
    https://www.greghendershot­t.com/2019/07/future-of-racket.html
  21. Kawa: Compiling Scheme to Java
    https://www.mit.edu/afs.new/sip­b/project/kawa/doc/kawa-tour.html
  22. Kawa in Languages shootout
    http://per.bothner.com/blog/2010/Kawa-in-shootout/
  23. Kawa 2.0 Supports Scheme R7RS
    https://developers.slashdot­.org/story/14/12/13/2259225/ka­wa-20-supports-scheme-r7rs/
  24. Kawa — fast scripting on the Java platform
    https://lwn.net/Articles/623349/
  25. Tail call (a její optimalizace)
    https://en.wikipedia.org/wi­ki/Tail_call
  26. SLIME (Wikipedia)
    http://en.wikipedia.org/wiki/SLIME
  27. slime.vim
    http://s3.amazonaws.com/mps/slime.vim
  28. What are the best scheme implementations?
    https://www.slant.co/topic­s/5282/~scheme-implementations
  29. Bigloo homepage
    http://www-sop.inria.fr/mimosa/fp/Bigloo/
  30. FTP s tarbally Bigloo
    ftp://ftp-sop.inria.fr/indes/fp/Bigloo
  31. GOTO 2018 • Functional Programming in 40 Minutes • Russ Olsen
    https://www.youtube.com/wat­ch?v=0if71HOyVjY
  32. TinyScheme (stránka na Sourceforge)
    http://tinyscheme.sourcefor­ge.net/home.html
  33. Embedding Tiny Scheme in a Game
    http://www.silicondelight­.com/embedding-tiny-scheme-in-a-game/
  34. Embedding Scheme for a game mission scripting DSL
    http://carloscarrasco.com/embedding-scheme-for-a-game-mission-scripting-dsl.html
  35. Všechny verze TinyScheme na SourceForge
    https://sourceforge.net/pro­jects/tinyscheme/files/ti­nyscheme/
  36. Fork TinyScheme na GitHubu
    https://github.com/yawnt/tinyscheme
  37. Ackermannova funkce
    https://cs.wikipedia.org/wi­ki/Ackermannova_funkce
  38. Ackermann function na Rosetta Code
    https://rosettacode.org/wi­ki/Ackermann_function#Sche­me
  39. Success Stories (lisp.org)
    https://lisp-lang.org/success/
  40. Allegro Common Lisp Success Stories
    https://franz.com/success/
  41. Clojure Success Stories
    https://clojure.org/commu­nity/success_stories
  42. Scheme Quick Reference
    https://www.st.cs.uni-saarland.de/edu/config-ss04/scheme-quickref.pdf
  43. Slajdy o Scheme (od slajdu číslo 15)
    https://docs.google.com/pre­sentation/d/1abmDnKjrq1tcjGvvRNAK­hOiSTSE2lyagtcEPal07Gbo/e­dit
  44. Scheme Cheat Sheet
    https://github.com/smythp/scheme-cheat-sheet
  45. Embedding Lua, embedding Guile
    http://puntoblogspot.blog­spot.com/2013/04/embedding-lua-embedding-guile.html
  46. Lambda Papers
    https://en.wikisource.org/wi­ki/Lambda_Papers
  47. Revised7Report on the Algorithmic Language Scheme
    https://small.r7rs.org/at­tachment/r7rs.pdf
  48. 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/
  49. Why is Scheme my first language in university?
    https://softwareengineerin­g.stackexchange.com/questi­ons/115252/why-is-scheme-my-first-language-in-university
  50. The Perils of JavaSchools
    https://www.joelonsoftware­.com/2005/12/29/the-perils-of-javaschools-2/
  51. How to Design Programs, Second Edition
    https://htdp.org/2019–02–24/index.html
  52. LilyPond
    http://lilypond.org/
  53. LilyPond — Extending (přes Scheme)
    http://lilypond.org/doc/v2­.18/Documentation/extendin­g/scheme-tutorial
  54. Scheme in LilyPond
    http://lilypond.org/doc/v2­.18/Documentation/extendin­g/scheme-in-lilypond
  55. GnuCash
    http://www.gnucash.org/
  56. Custom Reports (in GNU Cash)
    https://wiki.gnucash.org/wi­ki/Custom_Reports
  57. Program by Design
    https://programbydesign.org/
  58. SchemePy
    https://pypi.org/project/SchemePy/
  59. 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
  60. femtolisp
    https://github.com/JeffBe­zanson/femtolisp
  61. (How to Write a (Lisp) Interpreter (in Python))
    http://norvig.com/lispy.html
  62. Repositář s Guile Emacsem
    http://git.hcoop.net/?p=bpt/guile.git
  63. Interacting with Guile Compound Data Types in C
    http://www.lonelycactus.com/gu­ilebook/x1555.html
  64. Calling Guile functions from C
    http://www.lonelycactus.com/gu­ilebook/c1204.html#SECCAL­LGUILEFUNC
  65. Arrays, and other compound data types
    http://www.lonelycactus.com/gu­ilebook/charrays.html
  66. Interacting with Guile Compound Data Types in C
    http://www.lonelycactus.com/gu­ilebook/x1555.html
  67. Guile Reference Manual
    https://www.gnu.org/softwa­re/guile/manual/html_node/in­dex.html
  68. Scheme: Summary of Common Syntax
    https://www.gnu.org/softwa­re/guile/manual/html_node/Syn­tax-Summary.html#Syntax-Summary
  69. Scripting with Guile: Extension language enhances C and Scheme
    https://www.ibm.com/develo­perworks/library/l-guile/index.html
  70. Having fun with Guile: a tutorial
    http://dustycloud.org/misc/guile-tutorial.html
  71. Guile: Loading Readline Support
    https://www.gnu.org/softwa­re/guile/manual/html_node/Lo­ading-Readline-Support.html#Loading-Readline-Support
  72. lispy
    https://pypi.org/project/lispy/
  73. Lython
    https://pypi.org/project/Lython/
  74. Lizpop
    https://pypi.org/project/lizpop/
  75. Budoucnost programovacích jazyků
    http://www.knesl.com/budoucnost-programovacich-jazyku
  76. LISP Prolog and Evolution
    http://blog.samibadawi.com/2013/05/lisp-prolog-and-evolution.html
  77. List of Lisp-family programming languages
    https://en.wikipedia.org/wi­ki/List_of_Lisp-family_programming_languages
  78. clojure_py na indexu PyPi
    https://pypi.python.org/py­pi/clojure_py
  79. PyClojure
    https://github.com/eigenhom­bre/PyClojure
  80. Hy na GitHubu
    https://github.com/hylang/hy
  81. Hy: The survival guide
    https://notes.pault.ag/hy-survival-guide/
  82. Hy běžící na monitoru terminálu společnosti Symbolics
    http://try-hy.appspot.com/
  83. Welcome to Hy’s documentation!
    http://docs.hylang.org/en/stable/
  84. Hy na PyPi
    https://pypi.org/project/hy/#des­cription
  85. Getting Hy on Python
    https://lwn.net/Articles/596626/
  86. Programming Can Be Fun with Hy
    https://opensourceforu.com/2014/02/pro­gramming-can-fun-hy/
  87. Přednáška o projektu Hy (pětiminutový lighttalk)
    http://blog.pault.ag/day/2013/04/02
  88. Hy (Wikipedia)
    https://en.wikipedia.org/wiki/Hy
  89. GNU Emacs Lisp Reference Manual: Point
    https://www.gnu.org/softwa­re/emacs/manual/html_node/e­lisp/Point.html
  90. GNU Emacs Lisp Reference Manual: Narrowing
    https://www.gnu.org/softwa­re/emacs/manual/html_node/e­lisp/Narrowing.html
  91. GNU Emacs Lisp Reference Manual: Functions that Create Markers
    https://www.gnu.org/softwa­re/emacs/manual/html_node/e­lisp/Creating-Markers.html
  92. GNU Emacs Lisp Reference Manual: Motion
    https://www.gnu.org/softwa­re/emacs/manual/html_node/e­lisp/Motion.html#Motion
  93. GNU Emacs Lisp Reference Manual: Basic Char Syntax
    https://www.gnu.org/softwa­re/emacs/manual/html_node/e­lisp/Basic-Char-Syntax.html
  94. Elisp: Sequence: List, Array
    http://ergoemacs.org/emac­s/elisp_list_vs_vector.html
  95. Elisp: Property List
    http://ergoemacs.org/emac­s/elisp_property_list.html
  96. Elisp: Hash Table
    http://ergoemacs.org/emac­s/elisp_hash_table.html
  97. Elisp: Association List
    http://ergoemacs.org/emac­s/elisp_association_list.html
  98. The mapcar Function (An Introduction to Programming in Emacs Lisp)
    https://www.gnu.org/softwa­re/emacs/manual/html_node/e­intr/mapcar.html
  99. Anaphoric macro
    https://en.wikipedia.org/wi­ki/Anaphoric_macro
  100. Some Common Lisp Loop Macro Examples
    https://www.youtube.com/wat­ch?v=3yl8o6r_omw
  101. A Guided Tour of Emacs
    https://www.gnu.org/softwa­re/emacs/tour/
  102. The Roots of Lisp
    http://www.paulgraham.com/ro­otsoflisp.html
  103. Evil (Emacs Wiki)
    https://www.emacswiki.org/emacs/Evil
  104. Evil (na GitHubu)
    https://github.com/emacs-evil/evil
  105. Evil (na stránkách repositáře MELPA)
    https://melpa.org/#/evil
  106. 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
  107. GNU Emacs (home page)
    https://www.gnu.org/software/emacs/
  108. GNU Emacs (texteditors.org)
    http://texteditors.org/cgi-bin/wiki.pl?GnuEmacs
  109. An Introduction To Using GDB Under Emacs
    http://tedlab.mit.edu/~dr/gdbin­tro.html
  110. An Introduction to Programming in Emacs Lisp
    https://www.gnu.org/softwa­re/emacs/manual/html_node/e­intr/index.html
  111. 27.6 Running Debuggers Under Emacs
    https://www.gnu.org/softwa­re/emacs/manual/html_node/e­macs/Debuggers.html
  112. GdbMode
    http://www.emacswiki.org/e­macs/GdbMode
  113. Emacs (Wikipedia)
    https://en.wikipedia.org/wiki/Emacs
  114. Emacs timeline
    http://www.jwz.org/doc/emacs-timeline.html
  115. Emacs Text Editors Family
    http://texteditors.org/cgi-bin/wiki.pl?EmacsFamily
  116. Vrapper aneb spojení možností Vimu a Eclipse
    https://mojefedora.cz/vrapper-aneb-spojeni-moznosti-vimu-a-eclipse/
  117. 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/
  118. Emacs/Evil-mode – A basic reference to using evil mode in Emacs
    http://www.aakarshnair.com/posts/emacs-evil-mode-cheatsheet
  119. From Vim to Emacs+Evil chaotic migration guide
    https://juanjoalvarez.net/es/de­tail/2014/sep/19/vim-emacsevil-chaotic-migration-guide/
  120. Introduction to evil-mode {video)
    https://www.youtube.com/wat­ch?v=PeVQwYUxYEg
  121. EINE (Emacs Wiki)
    http://www.emacswiki.org/emacs/EINE
  122. EINE (Texteditors.org)
    http://texteditors.org/cgi-bin/wiki.pl?EINE
  123. ZWEI (Emacs Wiki)
    http://www.emacswiki.org/emacs/ZWEI
  124. ZWEI (Texteditors.org)
    http://texteditors.org/cgi-bin/wiki.pl?ZWEI
  125. Zmacs (Wikipedia)
    https://en.wikipedia.org/wiki/Zmacs
  126. Zmacs (Texteditors.org)
    http://texteditors.org/cgi-bin/wiki.pl?Zmacs
  127. TecoEmacs (Emacs Wiki)
    http://www.emacswiki.org/e­macs/TecoEmacs
  128. Micro Emacs
    http://www.emacswiki.org/e­macs/MicroEmacs
  129. Micro Emacs (Wikipedia)
    https://en.wikipedia.org/wi­ki/MicroEMACS
  130. EmacsHistory
    http://www.emacswiki.org/e­macs/EmacsHistory
  131. Seznam editorů s ovládáním podobným Emacsu či kompatibilních s příkazy Emacsu
    http://www.finseth.com/emacs.html
  132. evil-numbers
    https://github.com/cofi/evil-numbers
  133. Debuggery a jejich nadstavby v Linuxu (1.část)
    http://fedora.cz/debuggery-a-jejich-nadstavby-v-linuxu/
  134. Debuggery a jejich nadstavby v Linuxu (2.část)
    http://fedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-2-cast/
  135. Debuggery a jejich nadstavby v Linuxu (3): Nemiver
    http://fedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-3-nemiver/
  136. Debuggery a jejich nadstavby v Linuxu (4): KDbg
    http://fedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-4-kdbg/
  137. 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/
  138. Org mode
    https://orgmode.org/
  139. The Org Manual
    https://orgmode.org/manual/index.html
  140. Kakoune (modální textový editor)
    http://kakoune.org/
  141. Vim-style keybinding in Emacs/Evil-mode
    https://gist.github.com/tro­yp/6b4c9e1c8670200c04c16036805773d8
  142. Emacs – jak začít
    http://www.abclinuxu.cz/clan­ky/navody/emacs-jak-zacit
  143. Programovací jazyk LISP a LISP machines
    https://www.root.cz/clanky/pro­gramovaci-jazyk-lisp-a-lisp-machines/
  144. Evil-surround
    https://github.com/emacs-evil/evil-surround
  145. Spacemacs
    http://spacemacs.org/
  146. Lisp: Common Lisp, Racket, Clojure, Emacs Lisp
    http://hyperpolyglot.org/lisp
  147. Common Lisp, Scheme, Clojure, And Elisp Compared
    http://irreal.org/blog/?p=725
  148. Does Elisp Suck?
    http://irreal.org/blog/?p=675
  149. Emacs pro mírně pokročilé (9): Elisp
    https://www.root.cz/clanky/emacs-elisp/
  150. 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/
  151. Clojure(Script) Interactive Development Environment that Rocks!
    https://github.com/clojure-emacs/cider
  152. An Introduction to Emacs Lisp
    https://harryrschwartz.com/2014/04/08/an-introduction-to-emacs-lisp.html
  153. Emergency Elisp
    http://steve-yegge.blogspot.com/2008/01/emergency-elisp.html
  154. Lambda calculus
    https://en.wikipedia.org/wi­ki/Lambda_calculus
  155. 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/
  156. Micro Manual LISP
    https://www.scribd.com/do­cument/54050141/Micro-Manual-LISP
  157. How Lisp Became God's Own Programming Language
    https://twobithistory.org/2018/10/14/lis­p.html
  158. History of Lisp
    http://jmc.stanford.edu/ar­ticles/lisp/lisp.pdf
  159. The Roots of Lisp
    http://languagelog.ldc.upen­n.edu/myl/llog/jmc.pdf
  160. Racket
    https://racket-lang.org/
  161. The Racket Manifesto
    http://felleisen.org/matthi­as/manifesto/
  162. MIT replaces Scheme with Python
    https://www.johndcook.com/blog/2009/03/26/mit-replaces-scheme-with-python/
  163. Adventures in Advanced Symbolic Programming
    http://groups.csail.mit.e­du/mac/users/gjs/6.945/
  164. Why MIT Switched from Scheme to Python (2009)
    https://news.ycombinator.com/i­tem?id=14167453
  165. Starodávná stránka XLispu
    http://www.xlisp.org/
  166. AutoLISP
    https://en.wikipedia.org/wi­ki/AutoLISP
  167. Seriál PicoLisp: minimalistický a výkonný interpret Lispu
    https://www.root.cz/serialy/picolisp-minimalisticky-a-vykonny-interpret-lispu/
  168. Common Lisp
    https://common-lisp.net/
  169. Getting Going with Common Lisp
    https://cliki.net/Getting%20Started
  170. Online Tutorial (Common Lisp)
    https://cliki.net/online%20tutorial
  171. Guile Emacs
    https://www.emacswiki.org/e­macs/GuileEmacs
  172. Guile Emacs History
    https://www.emacswiki.org/e­macs/GuileEmacsHistory
  173. Guile is a programming language
    https://www.gnu.org/software/guile/
  174. MIT Scheme
    http://groups.csail.mit.e­du/mac/projects/scheme/
  175. SIOD: Scheme in One Defun
    http://people.delphiforum­s.com/gjc//siod.html
  176. CommonLispForEmacs
    https://www.emacswiki.org/e­macs/CommonLispForEmacs
  177. Elisp: print, princ, prin1, format, message
    http://ergoemacs.org/emac­s/elisp_printing.html
  178. Special Forms in Lisp
    http://www.nhplace.com/ken­t/Papers/Special-Forms.html
  179. Basic Building Blocks in LISP
    https://www.tutorialspoin­t.com/lisp/lisp_basic_syn­tax.htm
  180. Introduction to LISP – University of Pittsburgh
    https://people.cs.pitt.edu/~mi­los/courses/cs2740/Lectures/Lis­pTutorial.pdf
  181. Why don't people use LISP
    https://forums.freebsd.org/threads/why-dont-people-use-lisp.24572/
  182. Structured program theorem
    https://en.wikipedia.org/wi­ki/Structured_program_the­orem
  183. Clojure: API Documentation
    https://clojure.org/api/api
  184. Tutorial for the Common Lisp Loop Macro
    http://www.ai.sri.com/pkarp/loop.html
  185. Common Lisp's Loop Macro Examples for Beginners
    http://www.unixuser.org/~e­uske/doc/cl/loop.html
  186. A modern list api for Emacs. No 'cl required.
    https://github.com/magnars/dash.el
  187. The LOOP Facility
    http://www.lispworks.com/do­cumentation/HyperSpec/Body/06_a­.htm
  188. Clojure.org: Vars and the Global Environment
    http://clojure.org/Vars
  189. Clojure.org: Refs and Transactions
    http://clojure.org/Refs
  190. Clojure.org: Atoms
    http://clojure.org/Atoms
  191. Clojure.org: Agents as Asynchronous Actions
    http://clojure.org/agents
  192. Transient Data Structureshttp://clojure.or­g/transients
  193. Dynamic Languages Strike Back
    http://steve-yegge.blogspot.cz/2008/05/dynamic-languages-strike-back.html
  194. Scripting: Higher Level Programming for the 21st Century
    http://www.tcl.tk/doc/scripting.html
  195. Clojure (na Wikipedia EN)
    http://en.wikipedia.org/wiki/Clojure
  196. Clojure (na Wikipedia CS)
    http://cs.wikipedia.org/wiki/Clojure
  197. SICP (The Structure and Interpretation of Computer Programs)
    http://mitpress.mit.edu/sicp/
  198. Pure function
    http://en.wikipedia.org/wi­ki/Pure_function
  199. Funkcionální programování
    http://cs.wikipedia.org/wi­ki/Funkcionální_programová­ní
  200. Jazyky Hy a Clojure-py: moderní dialekty LISPu určené pro Python VM
    https://www.root.cz/clanky/jazyky-hy-a-clojure-py-moderni-dialekty-lispu-urcene-pro-python-vm/
  201. Pixie: lehký skriptovací jazyk s „kouzelnými“ schopnostmi
    https://www.root.cz/clanky/pixie-lehky-skriptovaci-jazyk-s-kouzelnymi-schopnostmi/
  202. Programovací jazyk Pixie: funkce ze základní knihovny a použití FFI
    https://www.root.cz/clanky/pro­gramovaci-jazyk-pixie-funkce-ze-zakladni-knihovny-a-pouziti-ffi/
  203. Stránka projektu Jython
    http://www.jython.org/
  204. Jython (Wikipedia)
    https://en.wikipedia.org/wiki/Jython
  205. Scripting for the Java Platform (Wikipedia)
    https://en.wikipedia.org/wi­ki/Scripting_for_the_Java_Plat­form
  206. JSR 223: Scripting for the JavaTM Platform
    https://jcp.org/en/jsr/detail?id=223
  207. List of JVM languages
    https://en.wikipedia.org/wi­ki/List_of_JVM_languages
  208. The JavaTM Virtual Machine Specification, Second Edition
    http://java.sun.com/docs/bo­oks/jvms/second_edition/html/VMSpec­TOC.doc.html
  209. The class File Format
    http://java.sun.com/docs/bo­oks/jvms/second_edition/html/Clas­sFile.doc.html
  210. javap – The Java Class File Disassembler
    http://docs.oracle.com/ja­vase/1.4.2/docs/tooldocs/win­dows/javap.html
  211. javap-java-1.6.0-openjdk(1) – Linux man page
    http://linux.die.net/man/1/javap-java-1.6.0-openjdk
  212. Using javap
    http://www.idevelopment.in­fo/data/Programming/java/mis­cellaneous_java/Using_javap­.html
  213. Examine class files with the javap command
    http://www.techrepublic.com/ar­ticle/examine-class-files-with-the-javap-command/5815354

Autor článku

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