Lisp
Lisp (historicky LISP) je rodina multiparadigmatických programovacích jazyků s dlouhou historií. Jeho název je zkratka pro List processing (zpracování seznamů). Přestože se jedná spíše o akademický jazyk, používá se i na reálných projektech, např. v oboru umělé inteligence. Používá ho také například textový editor Emacs či konstrukční program AutoCAD.
Lisp byl původně specifikován v roce 1958. V současné době se jedná o druhý nejstarší vysokoúrovňový jazyk, který se stále ještě používá v praxi; starší už je pouze Fortran. Lisp byl původně navržen jako programovací jazyk pro matematické výpočty a byl silně ovlivněn syntaxí Lambda kalkulu. Rychle se stal favorizovaným programovacím jazykem ve světě umělé inteligence. Lisp se stal průkopníkem v mnoha programových technikách, například: stromové struktury, automatická správa paměti nebo dynamické typování. Lisp nevnímá rozdíl mezi kódem a daty, díky čemuž má jednoduchou syntaxi. Celý program je tak složen z s-výrazů nebo ozávorkovaných seznamů ve tvaru (f a b c)
, kde na prvním místě je operátor/funkce a na dalších argumenty funkce. Všechny další funkce jazyka mají identickou syntaxi.
Z Lispu jsou odvozeny i další jazyky – například Tcl, Smalltalk nebo Scheme. Tvůrcem jazyka byl John McCarthy.
Syntaxe[editovat | editovat zdroj]
Základním zápisem v Lispu je seznam. Zapisuje se tímto způsobem:
(1 2 "ahoj" 13.2)
Tento seznam obsahuje čtyři prvky:
- celé číslo 1
- celé číslo 2
- řetězec znaků „ahoj“
- reálné číslo 13,2
Seznam v příkladu reprezentuje uspořádanou čtveřici. Závorky v jazyce Lisp nefungují tak jako v matematice, ale pouze označují začátek a konec seznamu.
Seznamy jsou v Lispu implementovány jako binární strom degenerovaný na jednosměrný spojový seznam.
Co se seznamem Lisp udělá, záleží na okolnostech.
Příkazy[editovat | editovat zdroj]
Příkazy jazyka Lisp se zapisují také jako seznam, jehož první prvek seznamu je název příkazu.
Například sčítání je realizováno příkazem +. Odpovídající konstrukce v jazyce vypadá takto:
(+ 1 2 3)
Interpret odpoví 6.
Ukázka kódu[editovat | editovat zdroj]
Program hello world lze zapsat několika způsoby. Nejjednodušší vypadá takto:
(format t "Hello, World!")
Funkce se v Lispu definují klíčovým slovem defun:
(defun hello () (format t "~&Hello, World!~%")) (hello)
Na prvních dvou řádcích je definice funkce hello, na třetím řádku je tato funkce svým jménem zavolána.
Funkcím lze předávat i argumenty. V následujícím příkladu je ukázka funkce fact, která vypočítá faktoriál zadaného čísla:
(defun fact (n) (if (zerop n) 1 (* n (fact (- n 1)))))
Pro výpočet faktoriálu čísla 6 předáme tuto hodnotu jako argument funkci fact:
(fact 6)
Návratovou hodnotou funkce bude hodnota 720.
Makra[editovat | editovat zdroj]
Lisp má jako jeden z mála jazyků propracovaný systém maker, díky kterým lze velmi výrazným způsobem ovlivnit celý jazyk. Makra jsou nejprve načtena v READ části REPLu, následně je provedena makroexpanze (tu provádí preprocesor) a až poté je celý výraz vyhodnocen běžnou EVAL částí. Nemá smysl uvažovat o aplikaci makra, v době vyhodnocení výrazu již žádné makro neexistuje. Makro pouze přepisuje text/kód předtím, než se předhodí k vlastnímu vyhodnocení. Zásadní rozdíl mezi makrem a funkcí pak je, že makro nevyhodnocuje své argumenty při zavolání funkce.
Quote, Unquote, Quasiquote[editovat | editovat zdroj]
Abychom mohli makra vůbec používat, musíme mít nějaké nástroje k transformaci kódu. Běžně se používá speciální operátor quote
, který vrátí následný výraz tak jak mu ho předáme — žádnou část nevyhodnotí. Jako syntaktickou zkratku můžeme použít apostrof '
.
;; Mohlo by se zdát, že quote není potřebný operátor, když máme list, ;; ale jak je vidět, je mezi nimi zásadní rozdíl — funkce list vyhodnocuje ;; všechny své argumenty, quote nevyhodnotí nic. > (quote (1 2 3)) (1 2 3) > (list 1 2 3) (1 2 3) > (quote (1 (+ 2 3 4) 5)) (1 (+ 2 3 4) 5) > (list 1 (+ 2 3 4) 5) (1 9 5) > (quote (a b c d)) (A B C D) > (list a b c d) Error: The variable A is unbound. > '(1 2 3) (1 2 3)
Abychom mohli i kvotované části nechat něco vyhodnotit, musíme mít mechanismus, kterým zrušíme ono kvotování a vrátíme se zpět k vyhodnocování. K tomu slouží speciální operátory unquote
a quasiquote
. Quasiquote se chová stejně jako quote, pouze s tím rozdílem, že ve svém těle umožňuje použít unquote, který vyhodnotí daný výraz. Syntaktická zkratka pro unquote je čárka ,
a pro quasiquote zpětný apostrof `
.
> `(1 2 ,(+ 3 4)) (1 2 7) > `(list 1 2 ,(list 3 4)) (LIST 1 2 (3 4)) > `('a 'b ,(list (+ 1 2) (+ 3 4)) c d) ((QUOTE A) (QUOTE B) (3 7) C D)
Základní práce s makry[editovat | editovat zdroj]
Makra se vytvářejí pomocí speciálního operátoru defmacro
. Nejjednodušší příklad může být definice vlastní podmínky, vlastního ifu. Pomocí makra by to vypadalo následovně:
(defmacro my-if (cond true false) `(if ,cond ,true ,false))
Makro se chová stejně jako běžný if:
> (my-if 1 2 3) 2 ;; Makro vrátí dvě jedničky, protože jednou se vytiskne ;; a jednou se vrátí jako výsledek funkce print. > (my-if 1 (print 1) (print 2)) 1 1 > (my-if nil (print 1) (print 2)) 2 2
Při definici „vlastního“ ifu musíme použít makro, protože nevyhodnocuje své argumenty. Kdybychom nadefinovali if jako funkci, nechovalo by se to stejně, protože argumenty už by se vyhodnotily při volání funkce a tím pádem by se vždy vyhodnotily obě větve podmínky.
(defun my-bad-if (cond true false) (if cond true false)) ;; Příklady volání: ;; Zde proběhne vyhodnocení správně > (my-bad-if 1 2 3) 2 ;; Při tomto volání se chybně vytiskne jedna šestka > (my-bad-if 1 (print 5) (print 6)) 5 6 5
Problémy spojené s makry[editovat | editovat zdroj]
Při používání maker si musíme dávat pozor na dva klasické problémy – dvojí vyhodnocení a symbol capture. Představme si if, který v true větvi automaticky vrátí výsledek podmínky a ve false větvi vrátí předaný argument. Ukázka, jak by to mělo fungovat:
; 1 je true, tak vrátí 1 > (if-false 1 2) 1 ; Výsledný seznam je true, vrátí seznam > (if-false (member 2 '(1 2 3 4 5)) 'nic) (2 3 4 5) ; nil je false, vrátí symbol nic > (if-false (member nil '(1 2 3 4 5)) 'nic) NIC
Naivní implementace by mohla vypadat takto:
(defmacro if-false-1 (cond false) `(if ,cond ,cond ,false))
Toto makro zdánlivě funguje. Ovšem do doby, než na něj pustíme kód s vedlejším efektem:
;; Funguje jak má > (if-false-1 (member 2 '(1 2 3 4 5)) 'nic) (2 3 4 5) ;; Funguje jak má > (if-false-1 (member nil '(1 2 3 4 5)) 'nic) NIC ;; Makro incf zvyšuje hodnotu symbolu o jedna. ;; Nefunguje jak má — očekáváme, že volání vrátí dvojku. > (let ((a 1)) (if-false-1 (incf a) 'nic)) 3
Kód (incf a)
se v těle makra vyhodnotil dvakrát, proto nám to vrátí trojku. Kód po makroexpanzi vypadá takto:
(LET ((A 1)) (IF (INCF A) (INCF A) 'NIC))
Řešením je navázat vyhodnocenou podmínku na nějaký symbol:
(defmacro if-false-2 (cond false) `(let ((cond-help ,cond)) (if cond-help cond-help ,false)))
Teď už se výraz vyhodnotí pouze jednou:
> (let ((a 1)) (if-false-2 (incf a) 'nic)) 2
Externí odkazy[editovat | editovat zdroj]
- Obrázky, zvuky či videa k tématu Lisp na Wikimedia Commons
- Jemný úvod do LISPu
- (anglicky) ANSI Common Lisp Implementation
- (anglicky) Visual LISP – vývojové prostředky AutoCADu
- (anglicky) Practical Common Lisp