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ň