Lisp (historickyLISP) je rodina multiparadigmatickýchprogramovacích jazyků s dlouhou historií. Jeho název je zkratka proListprocessing (zpracování seznamů). Přestože se jedná spíše o akademický jazyk, používá se i na reálných projektech, např. v oboruumělé inteligence. Používá ho také napříkladtextový editorEmacs či konstrukční programAutoCAD.
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 pouzeFortran. 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říkladTcl,Smalltalk neboScheme. Tvůrcem jazyka bylJohn McCarthy.
Základním zápisem v Lispu je seznam. Zapisuje se tímto způsobem:
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 vmatematice, ale pouze označují začátek a konec seznamu.
Seznamy jsou v Lispu implementovány jakobinární strom degenerovaný na jednosměrnýspojový seznam.
Co se seznamem Lisp udělá, záleží na okolnostech.
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:
Interpret odpoví6.
Programhello world lze zapsat několika způsoby. Nejjednodušší vypadá takto:
Funkce se v Lispu definují klíčovým slovemdefun:
(defunhello()(formatt"~&Hello, World!~%"))(hello)
Na prvních dvou řádcích je definice funkcehello, 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 funkcefact, která vypočítáfaktoriál zadaného čísla:
(defunfact(n)(if(zeropn)1(*n(fact(-n1)))))
Pro výpočet faktoriálu čísla 6 předáme tuto hodnotu jako argument funkci fact:
Návratovou hodnotou funkce bude hodnota 720.
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.
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átorquote, 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(123))(123)>(list123)(123)>(quote(1(+234)5))(1(+234)5)>(list1(+234)5)(195)>(quote(abcd))(ABCD)>(listabcd)Error:ThevariableAisunbound.>'(123)(123)
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átoryunquote aquasiquote. 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`.
>`(12,(+34))(127)>`(list12,(list34))(LIST12(34))>`('a'b,(list(+12)(+34))cd)((QUOTEA)(QUOTEB)(37)CD)Makra se vytvářejí pomocí speciálního operátorudefmacro. Nejjednodušší příklad může být definice vlastní podmínky, vlastního ifu. Pomocí makra by to vypadalo následovně:
(defmacromy-if(condtruefalse)`(if,cond,true,false))
Makro se chová stejně jako běžný if:
>(my-if123)2;; Makro vrátí dvě jedničky, protože jednou se vytiskne;; a jednou se vrátí jako výsledek funkce print.>(my-if1(print1)(print2))11>(my-ifnil(print1)(print2))22
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.
(defunmy-bad-if(condtruefalse)(ifcondtruefalse));; Příklady volání:;; Zde proběhne vyhodnocení správně>(my-bad-if123)2;; Při tomto volání se chybně vytiskne jedna šestka>(my-bad-if1(print5)(print6))565
Při používání maker si musíme dávat pozor na dva klasické problémy –dvojí vyhodnocení asymbol 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-false12)1; Výsledný seznam je true, vrátí seznam>(if-false(member2'(12345))'nic)(2345); nil je false, vrátí symbol nic>(if-false(membernil'(12345))'nic)NIC
Naivní implementace by mohla vypadat takto:
(defmacroif-false-1(condfalse)`(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(member2'(12345))'nic)(2345);; Funguje jak má>(if-false-1(membernil'(12345))'nic)NIC;; Makro incf zvyšuje hodnotu symbolu o jedna.;; Nefunguje jak má — očekáváme, že volání vrátí dvojku.>(let((a1))(if-false-1(incfa)'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((A1))(IF(INCFA)(INCFA)'NIC))
Řešením je navázat vyhodnocenou podmínku na nějaký symbol:
(defmacroif-false-2(condfalse)`(let((cond-help,cond))(ifcond-helpcond-help,false)))
Teď už se výraz vyhodnotí pouze jednou:
>(let((a1))(if-false-2(incfa)'nic))2