Lisp I: Funkcionální programování a Lispworks

…aneb funkcionálně programuje i strejda Google!
16. 07. 2008

O čem je řeč

Lisp je multiparadigmový programovací jazyk, což znamená, že v Lispu můžete programovat jak se vám zrovna zachce. Máte chuť programovat objektově? OK, CLOS (Common Lisp Object System) je tady pro vás. Procedurálně? Není problém, vedlejší efekty taky umíme. Funkcionálně? Na to je ten jazyk přímo stavěný. Jak jsem právě naznačil, Lisp je převážně funkcionální jazyk, případně objektový. Ačkoliv v něm jde programovat imperativně/pro­cedurálně, nedělá se to. Na procedurální programování jsou jiné jazyky. My se tedy v tomto seriálu budeme zabývat funkcionálním programováním v Lispu. Později snad i objektovým pojetím.

Název Lisp vznikl z „List processing“ (zpracování seznamů), protože v Lispu máte jednu hlavní datovou strukturu, a tou je právě seznam. Lisp nepatří mezi příliš používané programovací jazyky, přesto se s ním občas můžeme setkat i v praxi. Stále se používá v oboru umělé inteligence nebo se s ním píší části AutoCADu. Lisp je ale přesto jeden z nejstarších stále se používajících programovacích jazyků. Nejčastěji se s ním ale přeci jen setkáte na akademické půdě.

Základní rysy funkcionálního programování

Funkcionální programování je nejvíce podobné matematickým funkcím a vzorečkům. Tedy postupné aplikování různých funkcí a operátorů, až se nakonec dopídíme ke konečnému výsledku. Funkcionální programování se vyznačuje také tím, že v něm nenajdete vedlejší efekt. Vedlejší efekt je jakýkoliv postup, který mění vnitřní stav programu; typicky třeba efekt uložení do proměnné, efekt přiřazení. Z jiných programovacích jazyků jistě znáte klasické prom = 10, případně prom := 50. To ve funkcionálním programování nenajdete. Ono to tam obecně ani není třeba, když máte program, kde na sebe postupně nabalujete jednotlivé funkce.

Tím, že ve funcionálním programování neexistuje vedlejší efekt, stávají se programy jednoduššími. Ve funkcích nevstupují do hry žádné další vlivy (například různé globální proměnné), a díky neexistenci cyklů se ani při zpracování seznamu (Lispový ekvivalent pole – o tom potom) nemusí čekat na zpracování předcházejícího prvku, ale mohou se zpracovávat všechny běhy cyklu zároveň (hodí se u vícejádrových procesorů). Jednoduše řečeno ;-).

Dalším hlavním rysem je, že takové jazyky neobsahují cykly. On takový cyklus nemá moc smysl – co chcete v těle cyklu provádět, když vedlejší efekt použít nemůžete? Nemůžete nic do ničeho uložit, nemůžete nic vypsat na obrazovku (to je taky vedlejší efekt) apod. Cykly ztrácejí ve funkcionálním programování význam. Namísto cyklů se tudíž používá rekurze. Rekurze je ve funkcionálním programování dost zásadní pojem, protože veškeré složitější operace se dělají pomocí nich. Stejně jako máte polovinu imperativního programu v nějakých cyklech, tak polovinu funkcionálního kódu zase máte v rekurzi. Jestliže se vám z duše protiví rekurzivní funkce, radši se do funkcionálního programování nepouštějte.

Nutno podotknout, že Lisp jako takový není čistě funkcionální (což už plyne z toho, že je multiparadigmový).

Další informace o funcionálním programování můžete nalézt na wikipedii.

Vývojové prostředí Lispu – Lispworks

Docela zásadní pro vývoj věcí v Lispu je prostředí. Za sebe mohu doporučit LispWorks nebo CLISP. Oba programy jsou multiplatformí, ale minimálně LispWorks za to platí krutou daň v podobě brutálního množství různých nespecifikovaných bugů. S CLIPSem jsem moc nepracoval, takže nemohu soudit. Popíši zde alespoň práci s LispWorks.

Na instalaci Lispworks není nic těžkého, takže rovnou přejdu k prostředí samotnému. Po otevření editoru máte před sebou okno Posluchače (Listener). Posluchač slouží k jednorázovém testování a volání programů. Můžete si snadno vyzkoušet, jak vlastně Lisp funguje, a to bez toho, aniž byste museli psát nějaké hlavičky programu nebo abyste museli program kompilovat. Můžete si vyzkoušet něco jednoduchého, například:

(+ 1 2 3)

Tento příkaz vám vrátí číslo šest. Syntaxi Lispu a příkazy Lispu budeme ale rozebírat později v dalších dílech.

K napsání programu budete potřebovat editor. Tento editor vyvoláte buď požadovanou ikonkou (hned první, ten zohlý list) nebo ho spustíte v Tools → editor. Toto okénko slouží jako editor pro psaní Lispových progamů. Lispworks také obsahují debugger, který se nejsnáze spouští přímo v Posluchači. Zobrazte si okno Posluchače (je to ta modročervená ikonka) a napište nějaký nesmysl. Program vám vyhodí chybu a aktivují se některé ikonky. Mezi nimi je i beruška, což je GUI debugger. Klikněte na ni a uvidíte seznam volaných funkcí s hodnotami parametrů. Trochu blbé je, že vám tam systém předhazuje i různé systémové funkce a systémové proměnné, které vás vůbec nezajímají. Při programování složitějších programů ale debugger užitečný nepochybně je.

Teď si trochu zaprogramujeme, abychom si ukázali další vymoženost, a tou je inspektor. Ten slouží k procházení obsahu proměnných. Inspektor se spíše hodí u OOP, kdy jeden objekt má mnoho různých vlastností apod. Občas se ale hodí i jindy. Vepište do Listeneru tento kód:

(setf prom 10)

Tento kód uloží do proměnné prom desítku. Následním napsáním prom vám LispWorks opravdu desítku vrátí. Zkuste si to. Hned také vyzkoušejte inspektor. Najděte na liště ikonku s mikroskopem a zelenou tečkou a klikněte na ni. Otevře se inspektor, který vám ukáže vše o proměnné prom. Ve funckionálním programování to ale moc nevyužijete, tam přeci vedlejší efekt přiřazení nepoužíváme ;-).

Hello World!

Co by to bylo za úvod do programovacího jazyku, kdyby tady nepadla zmíňka o tom, jak v něm vypsat slavnou větu „Hello World!“. Tak tedy zde je kód, který po vložení do Posluchače vrátí naši větu:

(format t "Hello World!")

Užijte si to, je to na dlouhou dobu poslední příkaz, který vypíše na obrazovku nějaký normální text.

Drobná poznámka na konec

Již jsem se zmínil o chybách v LispWorks. Ale on kromě chyb obsahuje i jiné záludnosti. Pokud budete ladit nějaký program a furt vám to bude vyhazovat nesmyslné chyby, zkuste práci uložit a LispWorks restartovat, ono to někdy pomůže. Nejčastější příčiny takových chyb je, že Lisp jako takový je dynamický jazyk. Když spouštíte nějaký program, systém si zkompiluje celý kód a uloží si všechny funkce. To má výhodu v tom, že nemusíte pokaždé kompilovat celý program, ale stačí klidně zkompilovat jen jednu funkci. Horší je to ale v případě, kdy vymažete z kódu nějakou funkci, ale ona vlastně zůstane v paměti a furt vám tam něco kazí. Pak se můžete furt rozčilovat, proč to háže tutéž chybu, když jste tu funkci odstranili, ale vy jste ji vlastně neodstranili ;-). Můžete buď funkci oddefinovat ručně nebo zrestarovat celé LispWorks. V různých případech bývají oba způsoby různě efektivní.

Zajímavost na konec: Online interpret Lispu ve Flashi.