Hlavní navigace

Racket: programovací jazyk a současně i platforma pro vývoj nových jazyků

Pavel Tišnovský

Dnes si představíme pravděpodobně nejrozsáhlejší a nejúplnější implementaci programovacího jazyka Scheme. Jedná se o jazyk Racket, který je dodáván i s interaktivním vývojovým prostředím a množstvím přídavných modulů.

Doba čtení: 41 minut

Sdílet

11. Povinné a nepovinné parametry anonymních funkcí

12. Povinné a nepovinné parametry pojmenovaných funkcí

13. Parametry anonymních funkcí explicitně specifikované svým jménem

14. Parametry pojmenovaných funkcí specifikované jménem

15. Pojmenované a nepojmenované funkce s různou aritou

16. Funkce jako hodnoty

17. Obsah následující části seriálu

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

19. Literatura

20. Odkazy na Internetu

1. Racket – programovací jazyk a současně i platforma pro vývoj nových jazyků

V dnešní části seriálu o rozsáhlém a možná i poněkud chaotickém světě lispovských programovacích jazyků se seznámíme se základními vlastnostmi systému nazvaného Racket. Naprostá většina programovacích jazyků (což většinou byly různé varianty Scheme či LISPu), s nimiž jsme se až doposud seznámili, sestávala z interpretru a/nebo překladače, který byl doplněn o interaktivní smyčku REPL a taktéž o základní podpůrné knihovny. Systém Racket se ovšem z tohoto ustáleného schématu poněkud vymyká, protože kromě již zmíněných modulů – interpretru, překladače, REPL, základních knihoven – obsahuje i integrované vývojové prostředí, opravdu rozsáhlé množství knihoven a navíc systém pro snadnou instalaci dalších knihoven (či možná lépe řečeno celých balíčků).

Poznámka: původně se Racket jmenoval PLT Scheme, takže asi nebude velkým překvapením, že většina základních vlastností Racketu je ze Scheme odvozeno.

Ale to není zdaleka vše, protože samotný programovací jazyk Racketu je poměrně snadno rozšiřitelný a modifikovatelný, takže vzniklo hned několik jeho variant. Kromě klasického dynamicky typovaného jazyka Scheme je tak možné použít jazyk s možností přesné deklarace datových typů, jazyk s infixovu notací zápisu aritmetických výrazů, dokonce i implementaci Algolu 60 atd. (samotný dialekt se vybírá pomocí řádku začínajícího na #lang, což uvidíme v demonstračních příkladech).

Dnes se však seznámíme především s první zmíněnou variantou programovacího jazyka Racket, přesněji řečeno s jedním dialektem programovacího jazyka Scheme s dynamickým typovým systémem (typ je odvozen od hodnoty, nemusí být specifikován u proměnné ani u parametru funkce). Uvidíme, že i „základní dialekt Scheme“ doznal několika užitečných rozšíření. Teprve v navazujících článcích si ukážeme i další rozšíření tohoto jazyka o další syntaxi a taktéž o novou sémantiku (což je důležitější, než samotný způsob zápisu).

Poznámka: možná to bude znít poněkud přízemně, ale jedním z nejdůležitějších praktických rozšíření, které v systému Racket nalezneme, souvisí se systémem modulů. Mnoho implementací LISPu a Scheme (možná i většina implementací) systém modulů buď vůbec neobsahuje, nebo ho má vyřešený dosti problematickým způsobem, například omezením na definici jmenných prostorů atd. Tímto neduhem dokonce částečně trpí i programovací Clojure; ostatně dotazy, jak vlastně přesně funguje systém jmenných prostorů v Clojure, jsou velmi časté (ostatně neexistencí rozumného systému modulů trpí i některé mainstreamové programovací jazyky).

Obrázek 1: Logo systému Racket.

2. Instalace systému Racket

Instalace celého systému Racket, včetně již výše zmíněného vývojového prostředí, je poměrně jednoduchá, protože postačuje ze stránky https://download.racket-lang.org/ stáhnout instalační soubor připravený pro vámi používaný operační systém, což je v případě Linuxu BASH script, na jehož konci je celý systém Racket zabalený. Tento soubor získáme například s využitím nástroje wget (nebo libovolným webovým prohlížečem):

$ wget https://mirror.racket-lang.org/installers/7.4/racket-7.4-x86_64-linux.sh

Po stažení tohoto souboru je možné ho spustit, a to dokonce i bez práv roota, ideálně pod novým uživatelem, který bude mít přístup jen k Racketu:

$ bash racket-7.4-x86_64-linux.sh 

Na začátku nám instalační skript položí několik otázek:

This program will extract and install Racket v7.4.
 
Note: the required diskspace for this installation is 523M.
 
Do you want a Unix-style distribution?
  In this distribution mode files go into different directories according
  to Unix conventions.  A "racket-uninstall" script will be generated
  to be used when you want to remove the installation.  If you say 'no',
  the whole Racket directory is kept in a single installation directory
  (movable and erasable), possibly with external links into it -- this is
  often more convenient, especially if you want to install multiple
  versions or keep it in your home directory.
Enter yes/no (default: no) >

Povšimněte si, že instalační skript nám nabízí dvě možnosti instalace. První takzvaná „unixová“ varianta spočívá v tom, že se Racket nainstaluje do běžné adresářové struktury, na kterou jsme zvyklí z Unixových systémů: spustitelné soubory budou uloženy v adresáři /usr/bin, manuálové stránky v adresáři /usr/share/man atd. atd. Pro tento typ instalace pochopitelně budete potřebovat práva roota a navíc koliduje se správcem balíčků dané distribuce. Předností je, že lze snadno vytvářet spustitelné skripty programované v Racketu. Tyto skripty musí mít nastaven příznak +x a musí začínat řádkem s klasickým „she-bangem“:

#!/usr/bin/env racket

Výhodnější ovšem může být, zejména ve chvíli, kdy Racket má používat jen jediný uživatel, alternativní způsob instalace, který spočívá v tom, že se Racket rozbalí do zvoleného adresáře. Tím může být například adresář /opt nebo je pochopitelně možné instalaci provést přímo do domovského adresáře toho uživatele, který instalaci provádí:

Enter yes/no (default: no) > no

Po odpovědi „no“ se instalační skript zeptá, do jakého adresáře se má tedy instalace provést:

Where do you want to install the "racket" directory tree?
  1 - /usr/racket [default]
  2 - /usr/local/racket
  3 - ~/racket (/home/tester/racket)
  4 - ./racket (here)
  Or enter a different "racket" directory to install in.
> 4
Poznámka: instalaci můžete provést jako já do adresáře /opt. Po takto nakonfigurované instalaci je potom dobré přidat cestu /opt/racket/bin do proměnné PATH, čímž se vám zjednoduší přístup ke spuštění interpretru, REPLu i samotného vývojového prostředí.

Samotná instalace proběhne poměrně rychle, a to i přesto, že se na disku obsadí relativně velké množství prostoru: více než 460 MB (což je skutečně hodně, i když samotný instalační skript hlásí dokonce ještě větší číslo):

Checking the integrity of the binary archive... ok.
    Unpacking into "/opt/racket" (Ctrl+C to abort)...
Done.
 
If you want to install new system links within the "bin", "man"
  and "share/applications" subdirectories of a common directory prefix
  (for example, "/usr/local") then enter the prefix of an existing
  directory that you want to use.  This might overwrite existing symlinks,
  but not files.

Adresářová struktura nainstalovaného systému Racket vypadá následovně:

.
├── bin
├── collects
│   ├── acks
│   ├── compiler
│   ├── data
│   ├── db
│   ├── dynext
│   ├── ffi
│   ├── file
│   ├── info
│   ├── info-domain
│   ├── json
│   ├── launcher
│   ├── net
│   ├── openssl
│   ├── pkg
│   ├── planet
│   ├── racket
│   ├── raco
│   ├── reader
│   ├── realm
│   ├── setup
│   ├── s-exp
│   ├── syntax
│   ├── version
│   └── xml
├── doc
│   ├── 2d
│   ├── acks
│   ├── algol60
│   ├── browser
│   ├── bug-report
│   ├── cards
│   ├── compatibility
│   ├── continue
│   ├── contract-profile
│   ├── cookies
│   ├── data
│   ├── datalog
│   ├── db
│   ├── deinprogramm
│   ├── demo-m1
│   ├── demo-m2
│   ├── demo-manual-m1
│   ├── demo-manual-m2
│   ├── demo-manual-s1
│   ├── demo-manual-s2
│   ├── demo-s1
│   ├── demo-s2
│   ├── distributed-places
│   ├── draw
│   ├── drracket
│   ├── drracket-tools
│   ├── ds-store
│   ├── dynext
│   ├── embedded-gui
│   ├── eopl
│   ├── errortrace
│   ├── file
│   ├── foreign
│   ├── framework
│   ├── frtime
│   ├── future-visualizer
│   ├── games
│   ├── getting-started
│   ├── gl-board-game
│   ├── graphics
│   ├── gui
│   ├── guide
│   ├── help
│   ├── htdp
│   ├── htdp-langs
│   ├── htdp-ptr
│   ├── html
│   ├── images
│   ├── inside
│   ├── json
│   ├── lazy
│   ├── license
│   ├── local-redirect
│   ├── macro-debugger
│   ├── make
│   ├── math
│   ├── more
│   ├── mrlib
│   ├── mysterx
│   ├── mzcom
│   ├── mzlib
│   ├── mzscheme
│   ├── net
│   ├── openssl
│   ├── optimization-coach
│   ├── option-contract
│   ├── osx-ssl
│   ├── parser-tools
│   ├── pict
│   ├── pict-snip
│   ├── picturing-programs
│   ├── pkg
│   ├── plai
│   ├── planet
│   ├── plot
│   ├── plt-installer
│   ├── preprocessor
│   ├── profile
│   ├── quick
│   ├── quickscript
│   ├── r5rs
│   ├── r6rs
│   ├── racket-cheat
│   ├── racklog
│   ├── rackunit
│   ├── raco
│   ├── readline
│   ├── redex
│   ├── reference
│   ├── release
│   ├── sasl
│   ├── scheme
│   ├── scribble
│   ├── scribble-pp
│   ├── scriblib
│   ├── search
│   ├── sgl
│   ├── slatex-wrap
│   ├── slideshow
│   ├── source-syntax
│   ├── srfi
│   ├── srfi-nf
│   ├── stepper
│   ├── string-constants
│   ├── style
│   ├── swindle
│   ├── syntax
│   ├── syntax-color
│   ├── teachpack
│   ├── test-engine
│   ├── tool
│   ├── tools
│   ├── trace
│   ├── ts-guide
│   ├── ts-reference
│   ├── turtles
│   ├── unix-socket
│   ├── version
│   ├── web-server
│   ├── web-server-internal
│   ├── win32-ssl
│   ├── xml
│   └── xrepl
├── etc
├── include
├── lib
├── man
│   └── man1
└── share
    ├── applications
    └── pkgs

Nastavení proměnné prostředí PATH takovým způsobem, aby bylo možné jednoduše spustit interpret i REPL:

$ whereis racket
racket: /opt/racket/bin/racket
 
$ export PATH=$PATH:/opt/racket/bin
 
$ whereis racket
racket: /opt/racket/bin/racket
 
$ racket --version
Welcome to Racket v7.4.
Poznámka: toto nastavení můžete provést i v konfiguračních skriptech .bashrc, .profile nebo .bash_profile:
export PATH=$PATH:~/bin:~/.local/bin/:/opt/go/bin:~/go/bin:/opt/racket/bin

3. Integrované vývojové prostředí a interaktivní smyčka REPL

Po doufejme že úspěšné instalaci Racketu máme hned několik možností, jak celý systém začít používat. Můžeme například spustit jeho integrované vývojové prostředí:

/opt/racket/bin $ ./drracket

Obrázek 2: Spuštění IDE systému Racket.

Prozatím si však vystačíme s interaktivní smyčkou REPL, kterou spustíme příkazem racket:

/opt/racket/bin $ ./racket
 
Welcome to Racket v7.4.
Poznámka: pokud navíc zadáme příkazu racket i jméno skriptu, ten bude spuštěn, takže kromě REPLu pracuje tento nástroj i jako interpret.

Obrázek 3: Výběr jazyka a dialektu.

Interaktivní smyčka je použitelná prakticky stejným způsobem, jako ostatní REPL, s nimiž jsme se až doposud seznámili ve článcích o jazycích Guile, TinyScheme, PicoLispu, systému Kawa atd. Jednotlivé formy jsou ihned po svém zápisu vyhodnocovány. Nejjednodušší je pochopitelně situace ve chvíli, kdy je celá forma zapsána na jediném řádku:

> (+ 1 1)
2
 
> (* 6 7)
42

Formy zapisované na více řádcích jsou vyhodnoceny až se zápisem poslední pravé kulaté závorky:

> (+
  1 2
  )
3

K dispozici je i nápověda, která se ovšem neotevře přímo v terminálu, ale ve webovém prohlížeči:

> (help random)
 
Loading help index...
Sending to web browser...
  file: /opt/racket/doc/reference/generic-numbers.html
  anchor: (def._((lib._racket/private/base..rkt)._random))

Obrázek 4: Nápověda zobrazená ve webovém prohlížeči.

Poznámka: kromě výchozího prohlížeče, kterým je Lynx je možné si nastavit i jiný prohlížeč, ovšem Lynx poměrně pěkně zapadá do konceptu vývoje z terminálu.

4. Základy programovacího jazyka Racket

Ve druhé části článku se seznámíme se základními vlastnostmi programovacího jazyka Racket. Ten vychází z jazyka Scheme, takže většina dále popsaných příkladů již může být čtenářům tohoto seriálu již dobře známa. Zaměříme se především na tři sice základní, ale o to důležitější součásti programovacího jazyka Racket:

  1. Práci s numerickými hodnotami a využitím celé takzvané „numerické věže“
  2. Definicí pojmenovaných i anonymních funkcí, včetně funkcí s proměnným počtem parametrů, nepovinnými parametry, tzv. keywords parametry atd. Funkce jsou základním stavebním prvkem funkcionálních jazyků, takže je dobré znát všechny jejich možnosti (některé vlastnosti lze plně využít pouze při přímém použití anonymních funkcí).
  3. Některými základními speciálními formami určenými pro ovlivnění běhu programu. Jedná se pochopitelně o podmínky a taktéž o programové smyčky.

5. Numerická věž a příslušné základní predikáty

S takzvanou „numerickou věží“ jsme se již v tomto seriálu setkali při popisu možností dalších dialektů programovacího jazyka Scheme. Připomeňme si ve stručnosti, že se jedná o hierarchii datových typů reprezentujících různé typy čísel. Na vrcholu této hierarchie stojí obecný typ number, pod ním leží komplexní čísla, dále čísla reálná, čísla racionální (zlomky) a nakonec čísla celá:

# Typ Význam
1 number libovolná obecná čísla
2 complex komplexní čísla
3 real reálná čísla
4 rational zlomky (racionální čísla)
5 integer celá čísla

Převody mezi numerickými typy jsou prováděny automaticky na základě vyhodnocovaného výrazu. Například v následujícím výrazu bylo nutné vyhodnotit výsledek jako komplexní číslo, protože jsme se snažili vypočítat druhou odmocninu ze záporného čísla:

(sqrt (/ 3.14159 (- (expt 2 32))))
 
0+2.704548801180264e-05i

V jazyce Racket dále existuje velké množství predikátů určených pro zjištění, jakého typu je daná hodnota (a zda se vůbec jedná o číslo). Povšimněte si, že některé predikáty slouží pro zjištění, zda se jedná o „přesná“ či naopak „nepřesná“ čísla. Mezi „přesná čísla“ patří ty hodnoty, jejichž reálná i imaginární část je buď celé číslo nebo zlomek (pozor na rozdíl mezi reálnou části komplexního čísla a reálným číslem):

# Predikát Stručný popis
1 number? test, zda se jedná o jakoukoli numerickou hodnotu
2 complex? test, zda se jedná o komplexní číslo
3 real? test, zda se jedná o reálné číslo
4 rational? test, zda se jedná o racionální číslo (zlomek)
5 integer? test, zda se jedná o celé číslo
6 exact-integer? test, zda se jedná o „přesné“ celé číslo
7 exact-nonnegative-integer? test, zda se jedná o „přesné“ celé nezáporné číslo
8 exact-positive-integer? test, zda se jedná o „přesné“ celé kladné číslo
9 inexact-real? test, zda se jedná o „nepřesné“ reálné číslo
     
10 zero? test na nulu
11 positive? test na kladné číslo
12 negative? test na záporné číslo
13 even? test na sudé číslo
14 odd? test na liché číslo
15 exact? test na libovolné „přesné“ číslo
16 inexact? test na libovolné „nepřesné“ číslo

6. Funkce jako základní stavební bloky programů

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 Racket 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 to být poněkud matoucí, ale funkce se v jazyku Racket (i v jeho dokumentaci) někdy označují poněkud obecnějším termínem procedury.

V této kapitole si popíšeme způsob tvorby pojmenovaných funkcí a v kapitole osmé 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 Racket velmi jednoduché – použije se speciální forma define, za níž se do seznamu zapíše jméno nově vytvářené funkce i jména jejích formálních parametrů. 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).

Poznámka: připomeňme si, že define je speciální formou z toho důvodu, že se jazyk Racket nesnaží i okamžité vyhodnocení jejích parametrů.

Hodnota posledního vyhodnoceného výrazu se 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ě:

(define ([jméno funkce] [formální parametry]) [tělo funkce])

Definice konkrétní pojmenované funkce bez parametrů (vrací konstantu 0) a její následné zavolání:

(define (nothing)
  0)
 
(nothing)
0

Postup vytvoření uživatelské funkce s jedním parametrem a jejího následného použití:

(define (square x) (* x x))
 
(square 42)
1764
 
(square (+ 1 2))
9
 
(+ (square 3) (square 4))
25

Samozřejmě je možné vytvořit i funkci víceparametrickou, zde konkrétně funkci pro výpočet hodnoty kvadratické funkce ve tvaru y=ax2+bx+c pro zadanou hodnotu x:

(define (quadratic a b c x) (+ (* a x x) (* b x) c))
 
(quadratic 1 0 0 1)
1
 
(quadratic 2 2 2 4)
42

Ve funkci je možné vytvořit blok s lokálními proměnnými formou let:

(define (square x)
  (let ((result (* x x)))
    result))

Ovšem let lze použít i mimo definici funkce pro vytvoření lokálních proměnných (přesněji řečeno lokálního navázání symbolu/symbolů na určité hodnoty):

(define (add x y)
    (+ x y))
 
(print
    (let ((x 10)
          (y 20))
          (add x y)))

7. Pojmenování uživatelských funkcí

V programovacím jazyku Racket 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 (?):

# Funkce (predikát)
1 exact?
2 inexact?
3 odd?
4 even?
5 zero?
6 positive?
7 negative?
8 eq?
9 eqv?
10 equal?
11 null?
12 number?

Také jsme se seznámili s konverzními funkcemi používajícími ve svém názvu dvojici znaků ->. Mnohdy se také můžeme setkat s tím, že se jméno uživatelské funkce skládá z více slov oddělených pomlčkou (-), která je v jiných programovacích jazycích většinou rezervována pro zápis operátoru rozdílu, popř. změny znaménka. V následujících příkladech je ukázáno, že jména uživatelských funkcí mohou opravdu obsahovat téměř jakýkoli nealfanumerický znak (výjimek je pouze několik, vypsány jsou pochopitelně v manuálu jazyka Racket a většinou se jedná o různé formy závorek a taktéž o znak #):

(define (>= x y)
    (or (> x y) (= x y))
)
 
; druhá možná definice
(define (>= x y)
    (not (< x y))
)

První přiblížení k tomu, jak by se mohl zapsat ternární výraz. Tento příklad však má jeden poměrně závažný nedostatek vyplývající z vlastnosti jazyka Racket (a obecně jakéhokoli lispovského jazyka). Dokážete přijít na to, o jaký nedostatek se jedná?

(define (?: podminka prvni-vyraz druhy-vyraz)
    (if podminka prvni-vyraz druhy-vyraz)
)
 
; test
(?: #t 1 2)
1
 
; další test
(?: #f 1 2)
2
 
; Při tisku jednotlivých slov lze namísto
; řetězců použít i takzvané symboly uvozené apostrofem
(?: (< 1 2) 'mensi 'vetsi)
mensi

(?: (< 2 1) 'mensi 'vetsi)
vetsi

Programátoři znalí Basicu :-) pravděpodobně znají operátor <> (nerovnost), který lze v jazyku Racket velmi jednoduše vytvořit jako uživatelskou funkci:

(define (<> x y)
    (not (= x y)))
 
(<> 1 2)
 #t
 
(<> 1 1)
 #f

Ovšem výše uvedenou funkci můžeme též zobecnit na libovolný typ parametrů:

(define (<> a b)
    (not (equal? a b)))
 
(<> 'a 'b)
#t
 
(<> "hello" "world")
#t
 
(<> "hello" "hello")
#f

8. Anonymní funkce

Kromě pojmenovaných funkcí popsaných v předchozích dvou kapitolách je možné v programovacím jazyce Racket, podobně jako v LISPu a pochopitelně 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:

(lambda ([formální parametry]) [tělo anonymní funkce])
; pouze vytvoření anonymní funkce bez
; jejího dalšího použití (umělý příklad, který
; nemá větší význam, protože se anonymní funkce
; nikde nevolá)
> (lambda (x) (* x x))
#
Poznámka: ve skutečnosti jsou možnosti specifikace parametrů ještě dále rozšířeny, což si popíšeme v navazujících kapitolách.

Ihned po vytvoření anonymní funkce je ji možné zavolat:

; vytvoření anonymní funkce s jejím následným
; zavoláním s parametrem 42
((lambda (x) (* x x)) 42)
1764

Příklad použití anonymní funkce s více parametry:

; anonymní funkce s více parametry
(lambda (a b c) (+ a b c))
#<procedure #f (a b c)>
 
((lambda (a b c) (+ a b c)) 1 2 3)
6

Mezi funkcemi pojmenovanými a anonymními existuje velmi úzká vazba, kterou si můžeme vysvětlit na jednoduchém příkladu. Mějme uživatelskou funkci nazvanou plus, která sečte své dva parametry (pro jednoduchost považujme tyto parametry vždy za čísla) a vrátí součet hodnot obou parametrů. Definice takové funkce je velmi jednoduchá:

(define (plus x y) (+ x y))
 
; test
(plus 1 2)
3

Výše uvedený zápis je ekvivalentní s následujícím zápisem, ve kterém se vytváří proměnná nazvaná plus, která jako svoji hodnotu obsahuje (anonymní) funkci. Již v úvodním článku o programovacím jazyku Scheme jsme si řekli, že funkce lze používat na stejných místech jako hodnoty jiných typů, takže je tento zápis korektní (a to i v Racketu, který je, jak již víme, ze Scheme odvozen):

(define plus (lambda (x y) (+ x y)))
 
; zjistíme, jaká hodnota je na symbol plus navázána
plus
#<procedure:plus>
; test
(plus 1 2)
3
Poznámka: právě s posledním zápisem, který je kombinací speciálních forem define a lambda, se často setkáme v dalším textu i v navazujících částech tohoto seriálu.

9. Anonymní funkce s proměnným počtem parametrů

Kromě anonymních funkcí, v nichž jsou explicitně vyjmenovány všechny jejich parametry, lze v programovacím jazyku Racket vytvářet a následně i volat funkce s proměnným počtem parametrů, což může být v některých případech velmi užitečné. V nejjednodušším případě, pokud mají být všechny parametry proměnné (tj. ve skutečnosti se anonymní funkce nemusí volat s parametrem žádným) se používá následující způsob vytvoření anonymní funkce:

(lambda [jméno jediného formálního parametru] [tělo anonymní funkce])

To tedy znamená, že mezi následujícími dvěma výrazy je poměrně velký rozdíl:

(lambda (x) ...)
(lambda y ...)
Poznámka: první lambda výraz akceptuje jediný parametr nazvaný x, druhý lambda výraz pak libovolný počet parametrů, které se transformují do seznamu pojmenovaného pro změnu y.

Při volání druhé výše uvedené anonymní funkce se do formálního parametru y předá seznam obsahující všechny skutečně předávané parametry. S tímto seznamem je možné pracovat jako s kterýmkoli jiným seznamem, tj. například lze procházet přes jeho prvky atd:

; jeden ze způsobů vytvoření seznamu
((lambda x x) 1 2 3 4)
'(1 2 3 4)
 
; součet hodnot všech předaných parametrů
; (apply bude popsána dále)
((lambda x (apply + x)) 1 2 3 4)
10
 
; na parametr (seznam) lze aplikovat různé funkce
((lambda x (length x)) 'a 'b 'c 'd)
4

V programovacím jazyku Racket lze též použít kombinaci obou předchozích způsobů, tj. vytvoření anonymní funkce vyžadující pevný počet povinných parametrů s tím, že všechny ostatní hodnoty předané anonymní funkci jsou nepovinné. Všechny nepovinné hodnoty jsou při volání anonymní funkce uloženy do seznamu přiřazeného poslednímu parametru, přičemž tento parametr musí být při definici anonymní funkce od ostatních parametrů oddělen tečkou. Povšimněte si, že se v tomto případě nejedná o nějakou speciální syntaxi, kterou bylo nutné do jazyka zavést, ale pouze o využití již existujících možností Racketu, které podporuje, podobně jako LISP, explicitní zápis tečka-dvojic:

(lambda ([formální parametry].poslední parametr) [tělo anonymní funkce])

Následují příklady použití anonymní funkce s několika povinnými (pojmenovanými) parametry a možností předání dalších hodnot v seznamu předanému poslednímu parametru. Ve všech příkladech se v těle anonymní funkce pouze vytiskne obsah posledního „seznamového“ parametru:

((lambda (a . b) b) 1 2 3 4)
'(2 3 4)
 
((lambda (a b . c) c) 1 2 3 4)
'(3 4)
 
((lambda (a b c . d) d) 1 2 3 4)
'(4)
 
((lambda (a b c d . e) e) 1 2 3 4)
'()
Poznámka: programovací jazyk Racket, přesněji řečeno jeho interaktivní smyčka REPL, se nepatrně odlišuje od některých dalších implementací jazyka Scheme v tom ohledu, že seznamy, které jsou výsledkem nějakých výpočtů, jsou zobrazeny s apostrofem na začátku. Některé další interpretry Scheme namísto toho zobrazí obsah seznamu bez apostrofu, což znamená, že například předchozí příklad by při spuštění v REPLu vypadal následovně:
((lambda (a . b) b) 1 2 3 4)
(2 3 4)
 
((lambda (a b . c) c) 1 2 3 4)
(3 4)
 
((lambda (a b c . d) d) 1 2 3 4)
(4)
 
((lambda (a b c d . e) e) 1 2 3 4)
()

10. Pojmenované funkce s proměnným počtem parametrů

Vzhledem k tomu, že speciální formu define (ve variantě, kdy se definuje funkce) lze kdykoli zapsat s využitím speciální formy lambda, je v programovacím jazyku Racket možné 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ě:

(define (jméno funkce . parametr) [tělo funkce])

Což je ekvivalentní zápisu, který již známe z předchozího textu:

(define jméno funkce (lambda parametr [tělo funkce]))
Poznámka: v předchozím zápisu je důležité, že parametr není uzavřen do kulatých závorek tak, jak by tomu bylo v případě, kdyby se jednalo o klasický seznam parametrů.

Následuje příklad definice funkce s proměnným počtem parametrů:

; funkce vracející počet skutečně předaných parametrů
(define (foo . parametry) (length parametry))

Zavolání této funkce bez parametrů vrátí nulu:

(foo)
0

Předat můžeme jeden parametr:

(foo 42)
1

A samozřejmě i větší počet parametrů:

(foo 1 2)
2
 
(foo "bar" "baz")
2
 
(foo '(1 2 3 4))
1
Poznámka: v posledním příkladu byl předán jediný parametr (kterým je čistě náhodou seznam), proto se vrátila jednička.

Ukažme si ještě alternativní formu zápisu využívající kombinace define a lambda:

; alternativní forma zápisu
(define foo (lambda parametry (length parametry)))
 
; volání funkce bez parametrů
(foo)
0
 
; volání funkce se třemi parametry (zde se jedná o trojici symbolů)
(foo 'a 'b 'c)
3

11. Povinné a nepovinné parametry anonymních funkcí

Při deklaraci anonymních funkcí můžeme použít i takzvané nepovinné parametry. Pokud se při volání anonymní funkce neuvede hodnota takového parametru, bude za ni dosazena výchozí hodnota. Nepovinné parametry se poznají snadno – jsou zapsány formou vektoru o dvou prvcích. Prvním prvkem vektoru je jméno parametru, druhým prvkem pak výchozí hodnota nepovinného parametru.

Příklad anonymní funkce s jediným parametrem, který je nepovinný a jehož výchozí hodnota je rovna dvěma:

(lambda ([parametr 2]) ...tělo anonymní funkce...)

Příklad anonymní funkce s jedním povinným parametrem a jedním parametrem nepovinným:

(lambda (parametr-1 [parametr-2 2]) ...tělo anonymní funkce...)

Příklad anonymní funkce se dvěma nepovinnými parametry:

(lambda ([parametr-1 100] [parametr-2 200]) ...tělo anonymní funkce...)
Poznámka: z předchozích ukázek mj. vyplývá (logický) důsledek – nejprve musí být uvedeny všechny povinné parametry a teprve poté případné parametry nepovinné.

V další ukázce je deklarována funkce inc zvyšující hodnotu svého (povinného) parametru buď o jedničku nebo o uvedenou nepovinnou hodnotu delta:

#lang racket/base
 
(define inc
  (lambda (x [delta 1])
    (+ x delta)))
 
(display (inc 10))
(newline)
 
(display (inc 10 20))
(newline)
 
(display ((lambda (x [delta 1]) (+ x delta)) 10))
(newline)
 
(display ((lambda (x [delta 1]) (+ x delta)) 10 20))
(newline)

Výsledky:

11
30
11
30
Poznámka: povšimněte si, že celý skript začíná uvedením typu jazyka a jeho dialektu. Podobnými řádky budou začínat i všechny demonstrační příklady, s nimiž se setkáme v navazujícím textu.

12. Povinné a nepovinné parametry pojmenovaných funkcí

Podobně, jako u lambda výrazů, je možné povinné a nepovinné parametry použít i při deklaraci běžných pojmenovaných funkcí. Příklad z předchozí kapitoly, v němž jsme definovali funkci inc, tedy můžeme velmi snadno přepsat takto:

#lang racket/base
 
(define (inc x [delta 1])
    (+ x delta))
 
(display (inc 10))
(newline)
 
(display (inc 10 20))
(newline)

S výsledky:

11
30

Následuje poněkud složitější (a možná i praktičtější) příklad, v němž je deklarována funkce pro výpočet lineární transformace. Tato funkce akceptuje jeden povinný parametr x a dále dvojici nepovinných parametrů, které představují měřítko (hodnotu změny měřítka) a offset (posun). Výchozí hodnota měřítka je 1 (identita) a výchozí hodnota posunu je 0 (bez posunu):

#lang racket/base
 
(define (transform x [scale 1] [offset 0])
    (+ offset (* x scale)))
 
(display (transform 10))
(newline)
 
(display (transform 10 20))
(newline)
 
(display (transform 10 20 -50))
(newline)

Opět si ukažme výsledky použití této funkce:

10
200
150

13. Parametry anonymních 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.

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.

V dalším demonstračním příkladu se opět setkáme s funkcí určenou pro výpočet lineární transformace. Tentokrát jsou ovšem parametry s měřítkem a offsetem (posunem) deklarovány takovým způsobem, že je nutné je specifikovat přímo svým jménem, které začíná křížkem a dvojtečkou:

#lang racket/base
 
(define transform
  (lambda (x #:scale scale #:offset offset)
    (+ offset (* x scale))))

Příklad použití:

(display (transform 10 #:offset 0 #:scale 1))
(newline)
 
(display (transform 10 #:offset -100 #:scale 1))
(newline)
 
(display (transform 10 #:offset -100 #:scale 100))
(newline)
Poznámka: pojmenované parametry jsou vždy uváděny až za běžnými parametry nepovinnými.

Mnohem praktičtější je však kombinace obou předchozích možností – parametrů nepovinných a současně specifikovaných svým jménem. I to je v jazyku Racket pochopitelně možné, a to následujícím způsobem:

(define transform
  (lambda (x #:scale [scale 1] #:offset [offset 0])
    (+ offset (* x scale))))

Popř. pro větší názornost:

(define transform
  (lambda (x
           #:scale  [scale 1]
           #:offset [offset 0])
    (+ offset (* x scale))))

Příklad použití:

(display (transform 10 #:offset 0 #:scale 1))
(newline)
 
(display (transform 10))
(newline)
 
(display (transform 10 #:offset -10))
(newline)
 
(display (transform 10 #:offset -100 #:scale 1))
(newline)
 
(display (transform 10 #:offset -100 #:scale 100))
(newline)

14. Parametry pojmenovaných funkcí specifikované jménem

Tato kapitola bude velmi stručná, protože i u pojmenovaných funkcí je možné specifikovat parametry, které se při volání funkce musí uvádět společně se svým jménem. Následuje tedy ekvivalent prvního příkladu z předchozí kapitoly:

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

Taktéž můžeme vytvořit funkci akceptující nepovinné a současně i pojmenované parametry:

#lang racket/base
 
(define (transform x #:scale [scale 1] #:offset [offset 0])
    (+ offset (* x scale)))
 
(display (transform 10 #:offset 0 #:scale 1))
(newline)
 
(display (transform 10))
(newline)
 
(display (transform 10 #:offset -10))
(newline)
 
(display (transform 10 #:offset -100 #:scale 1))
(newline)
 
(display (transform 10 #:offset -100 #:scale 100))
(newline)

Výsledek předchozího skriptu:

10
10
0
-90
900

15. Pojmenované a nepojmenované funkce s různou aritou

Po přečtení předchozích sedmi kapitol by se mohlo zdát, že rozdíl mezi pojmenovanými a anonymními funkcemi je pouze nepatrný a spočívá v tom, že u pojmenovaných funkcí je nějakému symbolu funkce přiřazena a že tento symbol tedy funkci reprezentuje při jejím volání. Ve skutečnosti jsou však možnosti anonymních funkcí nepatrně větší a to z toho důvodu, že je možné vytvořit anonymní funkci s různou aritou. Ve skutečnosti se jedná o větší počet současně deklarovaných funkcí, které mají (mohou mít) různá těla podle toho, kolik parametrů se při volání funkce použije.

Podívejme se na příklad funkce, která akceptuje různý počet parametrů – od žádného parametru do dvou parametrů. Pro každou variantu je ve skutečnosti použito jiné tělo (tedy z pohledu interpretru naprosto odlišný kód):

#lang racket/base
 
(define inc
  (case-lambda
    [() 0]
    [(x) (+ x 1)]
    [(x delta) (+ x delta)]))
Poznámka: povšimněte si, že se v tomto případě namísto speciální formy lambda použije forma case-lambda.

Příklad použití této multifunkce:

(display (inc))
(newline)
 
(display (inc 10))
(newline)
 
(display (inc 10 -1))
(newline)
Poznámka: s podobným konceptem jsme se setkali i u programovacího jazyka Clojure, kde ovšem bylo možné vytvořit i pojmenované funkce s různou aritou:
(defn multiply
    ([x]
     (* x x))
    ([x y]
     (* x y))
    ([x y z] (* x y z)))

16. Funkce jako hodnoty

V programovacím jazyku Racket jsou funkce plnohodnotnými objekty, takže je například můžeme navázat na další symboly („uložit do proměnné“) atd. Ukažme si několik jednoduchých příkladů:

> (define plus +)
 
> (define add plus)
 
> (+ 1 2)
3
 
> (add 1 2)
3
 
> (plus 1 2)
3
 
> (add 1 2 3 4)
10

Funkce lze použít jako parametr jiné funkce (funkce vyššího řádu):

> (apply + '(1 2 3))
6
 
> (define plus +)
 
> (apply plus '(1 2 3))
6

Vytvoření nové funkce s využitím compose (podobné threading makru z Clojure):

> ((compose1 - sqr) 10)
-100
 
> ((compose1 sqr - sqr) 10)
10000

17. Obsah následující části seriálu

V navazující části seriálu o světě lispovských programovacích jazyků si ukážeme některé pokročilejší možnosti tohoto jazyka a především pak knihovny, které je možné ihned po instalaci Racketu použít. Jako ukázku si uvedeme program pro výpočet a vykreslení Mandelbrotovy množiny. Tento příklad byl získán ze stránky projektu Rosetta Code. V tomto příkladu je ukázáno použití knihovny pro práci s rastrovou grafikou, vytvoření rastrového obrázku ve formátu PNG, použití formy for* pro vytvoření zanořených programových smyček atd.:

#lang racket
 
(require racket/draw)
 
(define (iterations a z i)
  (define z′ (+ (* z z) a))
    (if (or (= i 255) (> (magnitude z′) 2))
        i
        (iterations a z′ (add1 i))))
 
(define (iter->color i)
  (if (= i 255)
      (make-object color% "black")
      (make-object color% (* 5 (modulo i 15)) (* 32 (modulo i 7)) (* 8 (modulo i 31)))))
 
(define (mandelbrot width height)
  (define target (make-bitmap width height))
    (define dc (new bitmap-dc% [bitmap target]))
      (for* ([x width] [y height])
             (define real-x (- (* 3.0 (/ x width)) 2.25))
             (define real-y (- (* 2.5 (/ y height)) 1.25))
             (send dc set-pen (iter->color (iterations (make-rectangular real-x real-y) 0 0)) 1 'solid)
             (send dc draw-point x y))
      (send target save-file "mandelbrot.png" 'png))
 
(mandelbrot 300 200)

Obrázek 5: Výsledek výpočtu provedeného předchozím skriptem.

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 ackermann.rkt rekurzivní výpočet Ackermannovy funkce https://github.com/tisnik/lisp-families/blob/master/racket/ac­kermann.rkt
2 basic.rkt základní použití interpretru jazyka Racket https://github.com/tisnik/lisp-families/blob/master/racket/basic.rkt
3 closure1.rkt použití uzávěrů, první příklad https://github.com/tisnik/lisp-families/blob/master/racket/clo­sure1.rkt
4 closure2.rkt použití uzávěrů, druhý příklad https://github.com/tisnik/lisp-families/blob/master/racket/clo­sure2.rkt
5 closure3.rkt použití uzávěrů, třetí příklad https://github.com/tisnik/lisp-families/blob/master/racket/clo­sure3.rkt
6 closure4.rkt použití uzávěrů, čtvrtý příklad https://github.com/tisnik/lisp-families/blob/master/racket/clo­sure4.rkt
7 factorial1.rkt rekurzivní výpočet faktoriálu s libovolným rozsahem výsledku https://github.com/tisnik/lisp-families/blob/master/racket/fac­torial1.rkt
8 factorial2.rkt úprava předchozího příkladu https://github.com/tisnik/lisp-families/blob/master/racket/fac­torial2.rkt
9 factorial3.rkt použití tail-call optimalizace https://github.com/tisnik/lisp-families/blob/master/racket/fac­torial3.rkt
10 functions1.rkt definice a volání funkcí, první příklad https://github.com/tisnik/lisp-families/blob/master/racket/fun­ctions1.rkt
11 functions2.rkt definice a volání funkcí, druhý příklad https://github.com/tisnik/lisp-families/blob/master/racket/fun­ctions2.rkt
12 functions3.rkt definice a volání funkcí, třetí příklad https://github.com/tisnik/lisp-families/blob/master/racket/fun­ctions3.rkt
13 functions4.rkt definice a volání funkcí, čtvrtý příklad https://github.com/tisnik/lisp-families/blob/master/racket/fun­ctions4.rkt
14 func_vararg.rkt funkce s proměnným počtem parametrů https://github.com/tisnik/lisp-families/blob/master/racket/fun­c_vararg.rkt
15 lambdas1.rkt lambda výrazy, první příklad https://github.com/tisnik/lisp-families/blob/master/racket/lam­bdas1.rkt
16 lambdas2.rkt lambda výrazy, druhý příklad https://github.com/tisnik/lisp-families/blob/master/racket/lam­bdas2.rkt
17 lambdas3.rkt lambda výrazy, třetí příklad https://github.com/tisnik/lisp-families/blob/master/racket/lam­bdas3.rkt
18 lambdas_vararg.rkt lambda výraz ss proměnným počtem parametrů https://github.com/tisnik/lisp-families/blob/master/racket/lam­bdas_vararg.rkt
19 numeric.rkt numerická věž jazyka Racket https://github.com/tisnik/lisp-families/blob/master/racket/nu­meric.rkt
20 pi1.rkt výpočet konstanty π v oboru reálných čísel https://github.com/tisnik/lisp-families/blob/master/racket/pi1.rkt
21 pi2.rkt výpočet konstanty π v oboru racionálních čísel https://github.com/tisnik/lisp-families/blob/master/racket/pi2.rkt
22 pi3.rkt dtto, ale převod výsledku na reálné číslo https://github.com/tisnik/lisp-families/blob/master/racket/pi3.rkt
23 functions_keyword_args.rkt funkce s explicitně pojmenovanými argumenty https://github.com/tisnik/lisp-families/blob/master/racket/fun­ctions_keyword_args.rkt
24 functions_keyword_optional_args.rkt funkce s explicitně pojmenovanými a současně i volitelnými argumenty https://github.com/tisnik/lisp-families/blob/master/racket/fun­ctions_keyword_optional_ar­gs.rkt
25 functions_optional_args1.rkt funkce s volitelnými argumenty, první příklad https://github.com/tisnik/lisp-families/blob/master/racket/fun­ctions_optional_args1.rkt
26 functions_optional_args2.rkt funkce s volitelnými argumenty, druhý příklad https://github.com/tisnik/lisp-families/blob/master/racket/fun­ctions_optional_args2.rkt
27 lambda_arity.rkt lambda výraz s proměnnou aritou https://github.com/tisnik/lisp-families/blob/master/racket/lam­bda_arity.rkt
28 lambda_keyword_args.rkt lambda výraz s explicitně pojmenovanými argumenty https://github.com/tisnik/lisp-families/blob/master/racket/lam­bda_keyword_args.rkt
29 lambda_keyword_optional_args.rkt lambda výraz s explicitně pojmenovanými a současně i volitelnými argumenty https://github.com/tisnik/lisp-families/blob/master/racket/lam­bda_keyword_optional_args­.rkt
30 lambda_optional_args.rkt lambda výraz s volitelnými argumenty https://github.com/tisnik/lisp-families/blob/master/racket/lam­bda_optional_args.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

20. Odkazy na Internetu

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