Funkce a lambda calculus -=-=-=-=-=-=-=-=-=-=-=-=-= ((lambda (foo) (bar foo)) (baz)) Už o Lispu víme hodně. Ale to nejdůležitější pořád ne. Neumíme vytvářed žádné funkce. Je možné provédst jednoduchý experiment. Napsat (write write). Uhádnete co se stane? No já vám napovím. Proměná write má hodnotu. Tou je jak víte funkce, která vypisuje na obrazovku. Tedy: ==> (write write) (lambda (obj . port) (#_write obj (if (null? port) (current-output-port) (car port))) ) () vypíše opravdu zajímavou věc. Jak už možná tušíte, jedná se o kód funkce write. Existuje dohoda, že listy začínající symbolem lambda jsou funkce. Jejich vyhodnocování v případě, že jsou na prvním místě seznamu probíhá následovně: vyhodnotí se všechny další prvky seznamu. Potom se provede binding lokálních proměných - vytvoří se lokální seznam jmen pro proměné a nastaví se podle seznamu následujícím těsně za lambda. V našem případě (obj . port). Tento seznam říká, že první parametr je obj. Tedy prvnímu parametru přiřadí jméno obj. Pak následuje . a port. To je konstrukce pro proměný počet parametrů. Pokud eval narazí na ., všechny případné další parametry pospojuje do listu a dá jim jedno jméno. Potom následuje seznam výrazů, které se vyhodnotí. Nakonec se lokální kontext pro proměné zruší a hodnota posledního výrazu se vrátí. Můžeme tedy vesele programovat: ==> ((lambda (a) (+ 1 2)(+ a 1)) 5) 6 Tady se vytvářím tzv. anonymní funkci. Místo abych ji přiřadil nějaké jméno, rovnou ji napíšu do volání. Má jeden parametr a, který se nastaví na 5. Potom se začne provádět tělo funkce. (+ 1 2) se vyhodnotí na 3 a výsledek se rovnou zapomene. Potom následuje (+ a 1). a se vyhodnotí na 5 a výsledek je tedy 6. Protože to už je poslední výraz v seznamu, provádění funkce se ukončí a vrátí se 6. No a to je celé. Teď už až na drobné detaily Lisp umíte. Ted se můžu pokusit vysvětlit včem práve zpočívá genialita Lispu. Zatím jsme se bavili o samých low level věcech. Přesto ale je pomocí nich možné vyvořit cokoliv. Je to právě tím, že program i data jsou to samé. Jakýkoliv program je totiž List. Díky tomu je možné vyvářet funkce za běhu. Proto lisp není procedurální jazyk. Je tedy možné například napsat funkci pro vytváření třídy. Ta vyvoří funkce pro jednotlivé metody. Ale nedá tam přímo kód té metody, ale kód, který podle vstupního parametru (instance objektu) zjistí, jakou metodu má zavolat a tu zavolá. Díky tomu je pak možné stejnojmené metody použít i pro jiné třídy aniž by došlo ke kolizi. Podobné takové příklady uvedu později. Aby syntaxe byla lidštější, má define rozšíření, kde můžete psát funkce, aniž byste vypisovali lambdu: ==> (define (inc a) (+ a 1)) inc ==> (write inc) (lambda (a) (+ a 1) ) () ==> (inc 1) 2 Řídící konstrukce v lispu a makra -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- Any programming language is at its best before it is implemented and used. Další geniálním nápadem lispu je to, že vůbec nemá speciální řídící konstrukce. Místo toho má makra. Například if v lispu funguje následovně: ==> (if (< 1 2) 'mensi 'vetsi) mensi if je cosi jako funkce. Před jeho voláním se ale listy nevyhodnocují, jako u funkce, ale předají se parametry ještě nevyhodnocené. if potom sám zavolá eval na první parametr. Podle toho jestli se vyhodnotí do true (#t ve scheme) vyhodnotí druhý parametry a pokud ne, tak třetí. První parametr je tedy podmínka, druhý then větev a třetí else. K vytváření maker slouží funkce defmacro. Celý princip je velmi podobný funkcím. Místo symbolu lambda se používá defmacro. Makra jsou ale mnohem mocnější, než ty v C, protože jsou zabudovány až do interpretru a tedy znají syntax vlastního jazyka. Jsou také mnohem robusnější. Téměř vše, o čem jsme se tu bavili, lze implementovat jako makra. Jako makra jde napsat i mnoho dalších věcí - debugger, smyčky apod. Nebudu to zde rozvádět, protože psaní maker není už úplně triviální záležitost. Důležité ale je to, že Lisp vlastně není jenom interpretr. Ale je to jakýsi interpretr intepretru, protože většina konstrukcí je v něm napsána. To dává lidem možnost, aby sami modifikovali jazyk tak, jak chtějí. Už samotná konstrukce defmacro je napsána v normální knihovně a tak implementována bez dalších konstrukcí. výheň