Některé zajímavé konstrukce -=-=-=-=-=-=-=-=-=-=-=-=-=-=- Within a computer, natural language is unnatural. Tak tady bych Vám rád načrtl možnosti Lispu na několika málo příkladech. Lispové knihovny se od těch v C hodně lišší. Jak už jsem ukázal v případě quicksortu, jejich použití je mnohem obratnější. I vlastní knihovny mohou být chytřejší. Lisp umí dobře pracovat s funkcemi s proměným počtem parametrů, umožňuje dělat funkce, které akceptují víc typů. Takže například knihovní funkce member může fungovat jak na listy, tak třeba na pole, stromy a další typy. Navíc Lisp umožňuje přidávat z knihoven další jazykove konstrukce. Jako je už zmíněná konstrukce cond, nebo objekty. Programování v Lispu je vlastně pouze volání různých funkcí. Existují funkce pro práci z různými typy. Standardní schemovská knihovna podporuje například následující: pole, asociované listy (prvky seznamu jsou přístupné pomocí klíčů. Například struktura udržující jměna proměných je asociovaný list - pro každé jméno má hodnotu), colections (to je cosi jako struktura, tedy obsahuje hodnoty přístupné přes klíče, ale navíc podporuje například pročítání hodnotu po hodnotě a další), dynamic, hashovací tabulky, chapter ordering (typ, který udržuje data ve stejném formátu jako kapitoly a podkapitoly v knize), object (podpora pro OOP), parameter list (slouží k předávání parametrů funkci), fronty, fronty s prioritou, rekordy , relační databáze, RB stromy a struktury. Jak vidíte, je z čeho vybírat. Nebudu tu všechno popisovat. Jenom bych rád na několika příkladech ukázal, jak to funguje. Jednotlivé typy ve scheme jsou většinou listy, ze speciální hlavičkou. (podobně jako fhunkce používají lambda). Například objekty používají #object OOP Objektově orientované programování je docela nový přístup k věci. Do některých jazyků byl dodatečně přidán (třeba do C). Většinou to potřebovalo dost změn a stejně se nejedná o objekty v pravém slova smyslu. Pouze o jakousi náhražku objektů. Ve scheme nic takového nutné nebylo. Zde se vytváří objekty knihovna. Těch existuje několik. Nejznámější je CLOS pro Common Lisp. Já tu mám implementaci jménem yasos (Yet Another Scheme Object System) a tak popíšu tu. Objekt v Lispovém pojetí je seřazený asociovaný seznam metod. Metody do objektu se přidávají pomocí make-method! a ruší pomocí unmake-metod!. Objekty mohou také dědit metody od libovolného množství předků. Vzniká následovně: ==> (define bagr (make-object)) bagr Za make-object může následovat seznam předchůdců. Funkce make-object sama vygeneruje kód objektu. Pro zajímavost vypadá následovně: #("object" (lambda (generic-method) (let ((method-def (assq generic-method method-list))) (if method-def (cdr method-def) #f)) ) (lambda (generic-method method) (set! method-list (cons (cons generic-method method) method-list)) method ) (lambda (generic-method) (set! method-list (object:removeq generic-method method-list)) #t ) (lambda () method-list )) Ale to nás naštěstí nemusí zajímat. Možná teď v tom máte trochu zmatek. V OOP se přece napřed vytvářejí třídy a potom teprve objekty. Tady mluvíme hned objektech. To je pravda. Pokud chci použít vytváření přes třídy, vytvořím pro trídu objekt a přidám konstruktora. To je normální metoda. Většinou se ve scheme jmenuje instantiate. K tomu ale musím vysvětlit, jak fungují metody ve scheme. Volání cílových metod je jednoduché. Zavoláte prostě (metoda objekt parametry). Scheme ale metody nezná a volá funkce. Ty ale nelze definovat pro každý typ zvlášť. To se řeší tak, že se vytvoří funkce, která zjistí, jaký objekt má jako parametr a podle toho zpustí tu správnou metodu. K vytváření takove funkce slouží (make-generic-metod) ==> (define instantiate (make-generic-method)) instantiate Funkce instantiate potom vypadá takto: ==> (write instantiate) (lambda (obj . operands) (if (object? obj) (let ((object-method ((vector-ref obj 1) generic-method))) (if object-method (apply object-method (cons obj operands)) (slib:error "Method not supported: " obj))) (apply exception-procedure (cons obj operands))) ) () Nyní můžeme napsat metodu instantiate a přiřadit ji každému objektu, který je třída. To ale můžeme zautomatizovat a napsat si následující pomůcku: (define (make-instance-object . ancestors) (define self (apply make-object (map (lambda (obj) (instantiate obj)) ancestors))) (make-method! self instantiate (lambda (self) self)) self) Ta už zařídí prázdného konstruktura každé instanci. Nyní máme třídy, instance, objekty, metody a dědičnost a tak už máme téměr. kompletní OOP. Nejdůležitější věc, kterou jsem tu přezkočil jsou proměné pro obejkt. Objekt ale má svůj lokílní binding a tak může mít privítní proměné. Sice postup je poněkud komplikovanější, než u čistě objektových jazyků, ale nemá narozdíl od C++ či pascalu žádné větší potíže. Navíc je tento postup velmi rozšiřitelný a další vymoženosti (jako privátní metody a public proměné) jde velmi snadno přidat. (ve standardní implementaci samozřejmě existují) Autoload Jak jsem říkal, lisp je založen na knihovnách. Není ale nejlepší nápad je po startu překladače všechny nahrát do paměti. Jedna metoda jak toto řešit je funkce, co daný soubor přihraje (require), ale je možné to i zautomatizovat. K tomu slouží autoload. Ten funguje tak, že pokud chcete mít nějakou funkci dostupnou, prostě napíšete defaultoload, jméno funkce a jméno modulu, co se má dohrát. On prostě vytvoří funkci, která sama daný modul dohraje a zavolá tu správnou. Tím se zruší a vše funguje normálně. Díky tomu je v Lispu možné psát cosi jako hlavičkové soubory. Nápověda Hodně lispů celkem elegantně přidává nápovědu. Do lambda listu ještě přidají místo pro text. Potom dodělají funkci help která daný text zobrazí a máte zdarma celkem šikovný systém pro dokumentování. Advice Další často používanou metodou pro rozšiřování programů je advice. Funguje to podobně jako dědičnost v OOP. Pokud potřebujete nějakou funkci rozšiřit, ptostě přidáte advice, což je funkce, která se může volad před, nebo po volání dané funkce. Tímto způsobem byl například do Emacs přidán Emacsspeak. Emacs je editor. Emacsspeak je podpora pro slepé. Umí číst to co je na obrazovce, ale velmi intelignetně. Pokud například posunete kurzor o slovo dobrava, přečte dané slovo. Toho je docíleno tak, že existuje funkce "posuň kurzor o slovo doprava" a Emacsspeak pouze přidá advici, která slovo přečte. Nemusí tedy dělat vůbec žádné zásahy do zdrojáků a přitom může velmi intelignetně číst, to co je na obrazovce. A to bude asi všechno. Není to zdaleka kompletní vyčet možností lispu, spíš ukázka toho, co v něm lze vytvořit. výheň