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]