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ň