Obsah
1. PicoLisp: minimalistický a přitom překvapivě výkonný interpret Lispu
2. Interní datové struktury používané interpretrem PicoLispu
4. Automatická správa paměti (garbage collector atd.)
5. Je interpret PicoLispu skutečně pojatý minimalisticky?
6. Základní vlastnosti PicoLispu – tečkové páry a seznamy
7. Další datové struktury postavené nad seznamy: zásobníky, fronty a cyklické seznamy
8. Funkce – základní stavební prvek programů
12. Reprezentace numerických hodnot
14. Příloha: stručná historie vývoje LISPu
1. PicoLisp: minimalistický a přitom překvapivě výkonný interpret Lispu
Programovací jazyk LISP za sebou má přibližně šedesátiletou historii, což mj. znamená, že se jedná o jednu z nejdéle existujících rodin programovacích jazyků vůbec (druhá podobná větev je tvořena různými verzemi Fortranu a třetí pak COBOLem). V průběhu oněch necelých šedesáti let vzniklo velké množství různých variant tohoto jazyka, přičemž některé varianty byly implementovány formou interpretru a jiné naopak byly vybaveny překladačem. Z rodiny LISPovských jazyků se vyvinuly i další podobně koncipované jazyky, především pak Scheme (v Linuxu zastupované především projektem Guile) a dnes stále více populární jazyk Clojure. Z „velkých“ LISPů je nutné jmenovat především dialekt Common Lisp. Kromě toho však vzniklo i mnoho minimalisticky pojatých interpretrů a překladačů Lispu. Patří sem například dnes již historický Xlisp, první (původně značně primitivní) AutoLISP a v neposlední řadě i PicoLisp, který si popíšeme dnes.
Interpret programovacího jazyka PicoLisp je – alespoň podle slov autorů tohoto projektu – pojat minimalisticky. To se ovšem týká především způsobu implementace interních datových struktur používaných samotným interpretrem a jeho správcem paměti (garbage collector). Naproti tomu vlastní programovací jazyk je až překvapivě vyspělý, což sice nemusí být na první pohled patrné, ale například při porovnání s podobnými minimalisticky pojatými programovacími jazyky (různými implementacemi Forthu) se ukazuje, že PicoLisp obsahuje jak rozsáhlou základní knihovnu, tak i možnost relativně snadného použití nativních (většinou céčkových) knihoven, které jsou v systému nainstalovány (dokonce existuje možnost propojení PicoLispu s běžící JVM, podle mě je to ale spíše technologické demo a nikoli řešení připravené pro použití v produkci).
Tento programovací jazyk je tedy v případě potřeby možné použít i ve funkci „lepidla“, v němž jsou implementovány pouze vysokoúrovňové části aplikace (tu lze navíc vytvářet a testovat interaktivně), zatímco části nízkoúrovňové mohou být vytvořeny v nějakém jazyku překládaném do nativního kódu. Na podobném předpokladu, tj. na myšlence současného použití vysokoúrovňového a nízkoúrovňového jazyka, byl ostatně postaven i dnes poněkud pozapomenutý jazyk TCL (viz též slavný článek od tvůrce tohoto jazyka, slova tohoto článku se už do značné míry vyplnila), ale i mnohem populárnější skriptovací jazyky (Python, Ruby, Perl, částečně i Lua).
2. Interní datové struktury používané interpretrem PicoLispu
Popisem možností programovacího jazyka PicoLisp se budeme poněkud podrobněji zabývat v navazujících kapitolách; nyní se pojďme alespoň ve stručnosti věnovat tomu, jakým způsobem je interně implementován vlastní interpret a jeho virtuální stroj. Interpretry LISPovských jazyků jsou většinou relativně jednoduché, zejména v porovnání s interpretry jiných programovacích jazyků se složitějšími syntaktickými pravidly. Je tomu tak z toho důvodu, že v LISPu se programy zapisují v notaci, která do značné míry koresponduje s AST (Abstract Syntax Tree), které si překladač či interpret prakticky jakéhokoli programovacího jazyka vytváří ve druhé fázi překladu. PicoLisp tedy parsuje zapsané formy a přímo z nich vytváří interní podobu programu, kterou ukládá do paměti a následně interpretuje.
To, že je interpret PicoLispu pojat minimalisticky, je způsobeno tím, že interně podporuje pouze několik jednoduchých datových typů a navíc jen jediný složený datový typ. Interně jsou totiž všechny hodnoty ukládány do takzvaných „tečkových párů“ (dot-pair, někdy se setkáme s překladem tečka-dvojice), což vlastně není nic jiného, než céčková datová struktura se dvěma prvky. První prvek se z historických důvodů jmenuje car, druhý pak cdr. Tyto prvky obsahují buď vlastní jednoduchou hodnotu (část celého čísla, hodnotu nil atd.) či ukazatel na další tečkový pár. Ve zdrojovém kódu PicoLispu vypadá deklarace tečkového páru (zde nazývaného buňka – cell) dosti nenápadně, ovšem síla plyne až ze způsobu použití:
typedef struct cell { struct cell *car; struct cell *cdr; } cell, *any;
3. Typ ukládaných hodnot
Typ hodnoty uložené do car či cdr se snadno pozná z nejnižších tří bitů. Předpokládejme překlad na 32bitové platformě (příkladem může být Raspberry Pi apod.). V takovém případě je sizeof(cell) rovno 64bitům a každý z ukazatelů má následující strukturu:
xxxxxxxxxxxxxxxxxxxxxxxxxxxxx010 Číslo (resp. jeho část) xxxxxxxxxxxxxxxxxxxxxxxxxxxxx100 Symbol xxxxxxxxxxxxxxxxxxxxxxxxxxxxx000 Pár (ukazatel)
Poznámky:
- PicoLisp pro čísla používá speciální formát umožňující reprezentovat prakticky jakoukoli hodnotu (omezeni jsme dostupnou kapacitou paměti). Pro malá čísla (do šedesáti bitů na 64bitové platformě) je hodnota přímo uložena v buňce (60 bitů hodnoty, bit pro znaménko následovaný již zmíněnou trojicí bitů 010), pro větší hodnoty pak v buňkách tvořících lineárně vázaný seznam.
- Nejnižší bit je interně použit správcem paměti typu mark & sweep. Implicitně je nulový (jak je naznačeno před tímto odstavcem), ovšem při označování dostupných buněk je správcem paměti nastavován na jedničku.
- To, že ukazatel na další pár má spodní tři bity (na 64bitových platformách čtyři bity) nulové, je zcela v pořádku, protože tečkové páry/buňky jsou vždy zarovnány na adresy dělitelné osmi (na 64bitových platformách šestnácti).
- V celém virtuálním stroji existuje jedna speciální hodnota (vytvořená při inicializaci VM) nazvaná NIL. Ta má hned několik významů; pokud je použita v buňce (což je velmi často), tak se jedná o ukazatel na symbol (tj. druhý typ hodnoty).
4. Automatická správa paměti (garbage collector atd.)
Žádné další složitější či rozsáhlejší datové struktury, než jsou výše zmíněné tečkové páry alias buňky, v PicoLispu nenajdeme. Co to však znamená v praxi? Největší dopad má tato vlastnost na implementaci správce paměti (garbage collector), který má do značné míry ulehčenou práci – nikdy totiž nemusí provádět přeskupování bloků paměti jen proto, aby se v ní vytvořilo místo například pro rozsáhlejší pole či pro delší řetězec (to je naopak poměrně velký problém například pro správce paměti v Javě atd. – velké objekty jsou mnohdy spravovány speciálním způsobem). Celá paměť, přesněji řečeno halda (heap), se v případě PicoLispu alokuje ve větších blocích, které mohou mít velikost například jeden megabajt (to je výchozí hodnota, ale při překladu ji lze změnit předeklarováním konstanty CELLS v hlavičkovém souboru pico.h – lze tak sledovat chování správce paměti):
#define CELLS (1024*1024/sizeof(cell)) // Heap allocation unit 1MB typedef struct heap { cell cells[CELLS]; struct heap *next; } heap;
Vidíme, že jednotlivé bloky tvoří lineárně vázaný seznam (další lineární seznam je použit pro svázání volných buněk v rámci bloků).
V těchto blocích je vytvořeno místo pro n tečkových párů (buněk), přičemž každé z míst může být buď skutečně obsazeno nebo je volné. Pokud je dané místo volné, obsahuje ukazatel na další volné místo. Všechna volná místa na haldě tak tvoří jediný lineárně vázaný seznam. Alokátor paměti tedy může jednoduše začít paměť přidělovat od začátku tohoto seznamu, pamatovat si ukazatel na první prvek seznamu a přesně od tohoto místa alokovat popř. hledat další volnou oblast paměti.
Správce paměti je typu mark & sweep. Ve fázi mark se označují dostupné tečkové páry (pro tento účel je vyhrazen nejnižší bit v každém slovu), ve fázi sweep se pak neoznačené tečkové páry zneplatňují a přidávají do seznamu volných buněk. Může dojít i k uvolnění celého bloku paměti (tj. jediným systémovým voláním se uvolní celý jeden megabajt). Mimochodem – celý správce paměti je implementován ve zdrojovém souboru o velikosti necelých čtyř kilobajtů a má jen 184 řádků, což skutečně je minimalistické pojetí (otázkou ovšem zůstává chování garbage collectoru u aplikací běžících například několik měsíců).
5. Je interpret PicoLispu skutečně pojatý minimalisticky?
Zdrojové kódy PicoLispu existují ve dvou variantách. Pro všechny 32bitové procesory (a pro architektury odlišné od x86_64) se používá varianta naprogramovaná v jazyce C, pro 64bitové procesory řady x86_64 pak varianta, v níž je část zdrojových kódů vytvořena v assembleru a je vůči céčkové variantě optimalizována. Podívejme se jen pro ilustraci na velikost výsledného binárního kódu interpretru a virtuálního stroje PicoLispu v porovnání s dalšími interpretry/VM.
Architektura x86_64, balíčky z Linux Mintu
# | Interpret/VM | Velikost |
---|---|---|
1 | picolisp | 195 272 |
2 | lua5.1 | 174 976 |
3 | lua5.2 | 195 416 |
4 | luajit | 445 080 |
5 | python2.7 | 3 345 416 |
6 | python3.4 | 3 709 944 |
Architektura ARM11, balíčky z Raspbiannu, PicoLisp přeložený autorem článku
# | Interpret/VM | Velikost |
---|---|---|
1 | picolisp (-O2) | 233 896 |
2 | picolisp (-Os) | 201 128 |
3 | lua5.1 | 128 920 |
4 | luajit | 365 312 |
5 | python2.7 | 2 674 536 |
6 | python3.4 | 2 814 320 |
Poznámky:
- Nejedná se o příliš férové porovnání, protože například nebereme v úvahu množství zabudovaných funkcí dostupných programátorům atd. (například v případě jazyka Lua nemáme k dispozici knihovnu pro práci s regulárními výrazy atd.)
- Raspbian nebyl zvolen náhodou, protože právě na Raspberry Pi má použití PicoLispu svůj význam. Na výkonnějších desktopech a serverech se vyplatí použít buď Common Lisp nebo rovnou Clojure.
- Na Raspbiannu se musí nepatrně upravit Makefile tak, aby se nepoužíval přepínač -m32.
- Na Raspbiannu jsem nejprve provedl překlad s výchozí volbou -O2 (optimalizace na výkon) a posléze i s volbou -Os (menší strojový kód).
- Povšimněte si, že ostatní binární soubory jsou pro ARM11 (ARMv6) menší, což může být způsobeno použitím instrukční sady Thumb, popř. méně agresivními optimalizacemi (rozbalování programových smyček atd.)
6. Základní vlastnosti PicoLispu – tečkové páry a seznamy
Víme již, že základní interní strukturou PicoLispu je takzvaný tečkový pár (dotted-pair). Tečkový pár je možné v LISPovských programech zapisovat formou dvojice výrazů (takzvaných S-výrazů) oddělených tečkou (a většinou i mezerami, v případě čísel jsou mezery nutností), které jsou uzavřeny do kulatých závorek (i když je pravda, že se s tečka-dvojicemi v reálných programech příliš často nesetkáme, především z důvodu nepřehledného zápisu s velkým množstvím závorek). V následujících ukázkách je vždy první řádek zapsán programátorem, druhý řádek je vyhodnocen interpretrem PicoLispu:
Základní způsob zápisu tečkového páru:
(1 . 2) (1 . 2)
Musí se skutečně jednat o pár, což je hlídáno:
(1 . 2 . 3) (1 . 2) -- Bad dotted pair
Složitější datová struktura tvořená rekurzivně zanořenými tečkovými páry:
(1 . ((2 . 3) . 4)) (1 (2 . 3) . 4)
Zde se opět pokoušíme o vytvoření trojice:
(1 . (2 . 3) . 4) (1 2 . 3) -- Bad dotted pair
Někdy je nutné interpretru zakázat pokus o vyhodnocení zadáním apostrofu:
'((1 . 2) . (3 . 4)) ((1 . 2) 3 . 4)
A nyní přichází na řadu velmi zajímavá vlastnost: rekurzivní tečkové páry zakončené dvojicí (atom . NIL) vytvoří seznam, resp. přesněji řečeno seznam je pouze syntaktickým cukrem pro takto vytvořenou datovou strukturu:
(1 . (2 . (3 . NIL))) (1 2 3)
Dtto platí při použití prázdného seznamu jako ukončovacího prvku (prázdný seznam je při zpracování a vyhodnocování totožný s NIL):
(1 . (2 . (3 . ()))) (1 2 3)
V PicoLispu se seznam začínající číslem nijak nevyhodnocuje (což je praktické):
(1 2 3 4) (1 2 3 4)
Seznam je možné uložit do proměnné, přesněji řečeno ho navázat na symbol a získat jeho první prvek funkcí car, ostatní prvky seznamu bez prvku prvního funkcí cdr, popř. použít některou z kombinací c[ad]+r
(setq a '(1 2 3)) (1 2 3) (car a) 1 (cdr a) (2 3) (cadr a) 2 (cddr a) (3)
Poznámka: povšimněte si rozdílu mezi (1 2 3 4) a (car a). V prvním případě se seznam nijak speciálně nevyhodnocuje, zatímco v případě druhém je první prvek chápán jako jméno funkce, která se má zavolat. Jinými slovy – v LISPu není velký rozdíl mezi daty a programovým kódem. Této důležité vlastnosti se říká homoikonicita.
7. Další datové struktury postavené nad seznamy: zásobníky, fronty a cyklické seznamy
Seznamy mohou být použity i pro implementaci dalších užitečných datových struktur. Příkladem může být zásobník (stack) se základními operacemi push a pop:
(push 'zasobnik 1) 1 (push 'zasobnik 2) 2 (push 'zasobnik 3) 3 zasobnik (3 2 1) (pop 'zasobnik) 3 (pop 'zasobnik) 2 (pop 'zasobnik) 1 (pop 'zasobnik) NIL
Třetí často používanou datovou strukturou je fronta (queue)
(queue 'fronta 1) 1 (queue 'fronta 2) 2 (queue 'fronta 3) 3 fronta (1 2 3) (pop 'fronta) 1 fronta (2 3) (pop 'fronta) 2 (pop 'fronta) 3 (pop 'fronta) NIL
Vytvoření fronty typu FIFO s využitím cyklického seznamu (poslední prvek seznamu ukazuje na prvek první):
(fifo 'X 1) 1 (fifo 'X 2 3) 3 X (3 1 2 .) ; tečka před závorkou označuje cyklický seznam! (fifo 'X) 1 (fifo 'X) 2 X (3 .) (fifo 'X) 3 (fifo 'X) NIL
8. Funkce – základní stavební prvek programů
Podobně jako u každého dialektu programovacího jazyka LISP, i v případě PicoLispu se program skládá především z funkcí. Ty mohou být anonymní (nepojmenované) či naopak pojmenované. Nejprve se zabývejme pojmenovanými funkcemi, protože ty se chovají prakticky stejně, jako běžné funkce v jiných programovacích jazycích. Pojmenované funkce se definují pomocí de (zkratka od „define“), za nímž následuje jméno funkce. Každá funkce může mít libovolný počet parametrů, jejichž jména se uvádí v seznamu za pojmenováním funkce. Poslední částí formy de je tělo funkce, přičemž po zavolání funkce se vyhodnocená forma vrátí jako její výsledek (nikde se tedy nezapisuje slovo „return“ ani nic podobného):
(de add (x y) (+ x y))
Přehlednější je však zápis definice funkce na více řádků. První řádek obsahuje jméno, druhý pojmenované parametry, další řádky pak tělo funkce:
(de mul (x y) (* x y))
PicoLisp nedělá velké rozdíly mezi kulatými a hranatými závorkami, proto se můžeme přiblížit formátu známému z programovacího jazyka Clojure:
(de mul [x y] (* x y))
Zavolání funkce je jednoduché – používá se stále ten samý formát seznamu, na jehož prvním místě je jméno funkce a za ním následují parametry:
(mul 6 7) 42
Funkce bez parametrů, která vrací NIL:
(de nic () NIL) (nic) NIL
9. Funkce vyššího řádu
PicoLisp sice není, na rozdíl od Haskellu a částečně i Clojure, čistě funkcionální jazyk, nicméně i zde hrají při vývoji aplikací velkou roli funkce vyššího řádu, tj. funkce, které jako své parametry akceptují jiné funkce popř. dokonce vrací (nové) funkce jako svoji návratovou hodnotu. Mezi dvě základní funkce vyššího řádu, které nalezneme prakticky ve všech dialektech programovacího jazyka Lisp, patří funkce nazvané mapcar a taktéž apply. Funkce mapcar jako svůj první parametr akceptuje jinou funkci (s jedním parametrem) a druhým parametrem musí být seznam. mapcar postupně aplikuje předanou funkci na jednotlivé prvky seznamu a vytváří tak seznam nový (modifikovaný). Podívejme se na jednoduchý příklad – aplikace funkce pro zvýšení hodnoty o jedničku na seznam:
(de inc (x) (+ x 1)) (mapcar inc (1 2 3)) (2 3 4)
Funkce apply se chová poněkud odlišně – aplikuje totiž nějakou funkci (svůj první parametr) na předaný seznam. Typický „školní“ příklad s binární funkcí + (tj. funkcí se dvěma parametry) může vypadat následovně:
(apply + (1 2 3 4)) 10
Pozor však – apply není reduce a ve výše uvedeném příkladu fungovala proto, že funkce + je variadická. Následující kód již správně nebude:
(de add (x y) (+ x y)) (apply add (1 2 3 4)) 3
Můžeme zkusit i něco složitějšího. Funkcí range vygenerujeme seznam hodnot a posléze tyto hodnoty vynásobíme:
(range 1 10) (1 2 3 4 5 6 7 8 9 10) (apply * (range 1 6)) 720
Ono vynásobení hodnot od 1 do n není vlastně nic jiného, než výpočet faktoriálu, takže:
(de factorial (n) (apply * (range 1 n)))
10. Programové smyčky
Vzhledem k tomu, že se PicoLisp nesnaží být (na rozdíl od Scheme) čistě funkcionálním jazykem, nalezneme v něm i programové smyčky, které se ovšem stále zapisují formou funkcí či speciálních forem. Nebudeme si ukazovat všechny typy programových smyček, ale jen naprosté základy:
Počítaná smyčka typu for:
(for n 10 (println n)) 1 2 3 4 5 6 7 8 9 10
Počítaná smyčka typu while:
(while (num? (read)) (println 'cislo)) 1 cislo 454 cislo 4254 cislo konec
Počítaná smyčka typu repeat-until:
(until (=T (setq N (read))) (println 'vysledek (* N N))) 1 vysledek 1 4 vysledek 16 9 vysledek 81 10 vysledek 100
11. Anonymní funkce
Kromě pojmenovaných funkcí, které jsme si již představili v předchozích kapitolách, je možné v PicoLispu použít i funkce anonymní, tj. funkce, které nejsou navázány na žádné jméno. Zajímavé je, že zde nenalezneme žádnou obdobu speciální formy lambda ani nic podobného, ale lze použít formu quote, která se v PicoLispu chová poněkud odlišně, než v jiných dialektech LISPu. Podívejme se na typický příklad – budeme chtít ze vstupního seznamu vytvořit výstupní seznam s hodnotami o jedničku zvýšenými. Pro něco tak jednoduchého asi nemá smysl si vytvářet novou pojmenovanou funkci, ale použijeme přímo funkci anonymní:
(mapcar (quote (x) (+ x 1) ) (1 2 3 4)) (2 3 4 5)
Zajímá vás řada n2?:
(mapcar (quote (x) (* x x)) (range 1 10)) (1 4 9 16 25 36 49 64 81 100)
Nebo chcete získat seznam faktoriálů n od 1 do 10?:
(mapcar (quote (n) (apply * (range 1 n) )) (range 1 10)) (1 2 6 24 120 720 5040 40320 362880 3628800)
12. Reprezentace numerických hodnot
PicoLisp se od mnoha dalších dnes používaných programovacích jazyků odlišuje i tím, že pro reprezentaci numerických hodnot nepoužívá ani základní celočíselné datové typy (int, long int atd.) a dokonce ani dnes běžné formáty hodnot s plovoucí řádovou čárkou (float, double), které jsou definovány v normě IEEE 754. Namísto toho jsou podporována celá čísla se znaménkem, která mají neomezený rozsah. Interně jsou totiž číselné hodnoty reprezentovány speciálním typem seznamu, který vypadá následovně:
Number | V +-----+-----+ | DIG | | | +-----+--+--+ | V +-----+-----+ | DIG | | | +-----+--+--+ | V +-----+-----+ | DIG | / | +-----+-----+
Tato datová struktura je v 64bitové variantě PicoLispu mírně odlišná, než je tomu v základní 32bitové verzi, nicméně původní myšlenka zůstává zachována: pro relativně malé hodnoty (jak kladné, tak i záporné) postačuje číslo reprezentovat jedinou buňkou (cell), zatímco u větších hodnot je nutné použít dvě či dokonce větší množství buněk propojených formou lineárního seznamu. Předností je (podle očekávání) neomezený rozsah a taktéž nezávislost na existenci matematického koprocesoru (či příslušné knihovny), nevýhodou pak obecně poněkud pomalejší výpočty, zejména u těch mikroprocesorových architektur, které mají plnohodnotné matematické koprocesory. Naopak u malých mikrořadičů může být výhodnější NEpoužít matematickou knihovnu pro reálná čísla a namísto toho se spokojit s řešením nabízeným PicoLispem. Díky možnosti specifikovat pevné umístění řádové tečky (čárky) se navíc (prakticky) neomezený rozsah spojuje s neomezenou přesností, což je téma, které si ukážeme příště.
Podívejme se nyní na to, jak výpočty s „neomezenými“ celočíselnými hodnotami probíhají v praxi. Opět si zvolme populární výpočet faktoriálu:
(de factorial [n] (apply * (range 1 n)))
Vlastní výpočet faktoriálu je omezen jen výkonem mikroprocesoru (ten je dostatečný i na původním modelu Raspberry Pi) a dostupnou operační pamětí. Začneme zlehka:
(for n 10 (println n (factorial n))) 1 1 2 2 3 6 4 24 5 120 6 720 7 5040 8 40320 9 362880 10 3628800
Ovšem můžeme se například podívat na 1000!:
(factorial 1000) 402387260077093773543702433923003985719374864210714632543799 910429938512398629020592044208486969404800479988610197196058 631666872994808558901323829669944590997424504087073759918823 627727188732519779505950995276120874975462497043601418278094 646496291056393887437886487337119181045825783647849977012476 632889835955735432513185323958463075557409114262417474349347 553428646576611667797396668820291207379143853719588249808126 867838374559731746136085379534524221586593201928090878297308 431392844403281231558611036976801357304216168747609675871348 312025478589320767169132448426236131412508780208000261683151 027341827977704784635868170164365024153691398281264810213092 761244896359928705114964975419909342221566832572080821333186 116811553615836546984046708975602900950537616475847728421889 679646244945160765353408198901385442487984959953319101723355 556602139450399736280750137837615307127761926849034352625200 015888535147331611702103968175921510907788019393178114194545 257223865541461062892187960223838971476088506276862967146674 697562911234082439208160153780889893964518263243671616762179 168909779911903754031274622289988005195444414282012187361745 992642956581746628302955570299024324153181617210465832036786 906117260158783520751516284225540265170483304226143974286933 061690897968482590125458327168226458066526769958652682272807 075781391858178889652208164348344825993266043367660176999612 831860788386150279465955131156552036093988180612138558600301 435694527224206344631797460594682573103790084024432438465657 245014402821885252470935190620929023136493273497565513958720 559654228749774011413346962715422845862377387538230483865688 976461927383814900140767310446640259899490222221765904339901 886018566526485061799702356193897017860040811889729918311021 171229845901641921068884387121855646124960798722908519296819 372388642614839657382291123125024186649353143970137428531926 649875337218940694281434118520158014123344828015051399694290 153483077644569099073152433278288269864602789864321139083506 217095002597389863554277196742822248757586765752344220207573 630569498825087968928162753848863396909959826280956121450994 871701244516461260379029309120889086942028510640182154399457 156805941872748998094254742173582401063677404595741785160829 230135358081840096996372524230560855903700624271243416909004 153690105933983835777939410970027753472000000000000000000000 000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000
Tento výsledek by měl (alespoň doufejme) odpovídat výsledku z adresy http://justinwhite.com/big-calc/1000.html.
Další možnosti práce s numerickými hodnotami, především změnu pozice řádové tečky (čárky), si popíšeme ve druhé části tohoto článku.
13. Obsah druhé části článku
Ve druhé a současně i poslední části tohoto článku popis možností nabízených těm vývojářům, kteří se rozhodnou využít programovací jazyk PicoLisp, dokončíme. Zabývat se budeme jak funkcemi, které jsou součástí základních knihoven tohoto jazyka (včetně deklarací tříd či zpracování výjimek), tak i způsobem kooperace programů napsaných v PicoLispu s nativními knihovnami. Nezapomeneme ani na další zajímavou vlastnost PicoLispu – možnost volání metod objektů vytvořených v samostatně běžícím virtuálním stroji jazyka Java (JVM). Jak kooperace s nativními knihovnami, tak i volání metod v JVM jsou z hlediska praktické použitelnosti PicoLispu velmi důležité, protože nelze předpokládat (ani by to vlastně nebylo praktické), že pro tento dosti minoritní interpret s relativně malou komunitou vznikne větší množství knihoven a/nebo dokonce celých frameworků.
14. Příloha: stručná historie vývoje LISPu
Historie programovacího jazyka LISP je poměrně dlouhá a komplikovaná, neboť se jedná o jeden z nejstarších programovacích jazyků vůbec. Autorem teoretického návrhu LISPu je John McCarthy, který se již v roce 1956 připojil k týmu, jehož úkolem bylo navrhnout algebraický programovací jazyk umožňující zpracování seznamů, jenž by byl vhodný pro vývoj systémů umělé inteligence – AI (zatímco dnes jsou „v kurzu“ enterprise systémy, cloudy, virtualizace, popř. WEB 2.0, v padesátých a šedesátých letech minulého století se jednalo o umělou inteligenci a taktéž o expertní systémy). McCarthy navrhl, aby se fakta o okolním světě (která může AI při své činnosti použít) reprezentovala formou vět ve vhodně strukturovaném formálním jazyce. Posléze se ukázalo, že je výhodné reprezentovat jednotlivé věty formou seznamů. McCarthy myšlenku jazyka vhodného pro AI rozpracoval dále – odklonil se například od infixové notace zápisu algebraických výrazů, protože naprogramování některých manipulací s těmito výrazy (derivace, integrace, zjednodušení výrazů, logická dedukce) bylo zbytečně složité.
Následně McCarthy ve svých teoretických pracech (vznikajících v průběhu let 1957 a 1958) ukázal, že je možné s využitím několika poměrně jednoduchých operací (a notací pro zápis funkcí) vytvořit programovací jazyk, který je Turingovsky kompletní (tj. jeho výpočetní mocnost je ekvivalentní Turingovu stroji), ale zápis algoritmů v tomto jazyce je mnohem jednodušší než zápis pravidel pro Turingův stroj. Tento jazyk, jenž byl z velké části založen na Lambda kalkulu, obsahoval možnost vytváření rekurzivních funkcí (což byl významný rozdíl například oproti tehdejší verzi FORTRANU), funkce bylo možné použít jako argumenty jiných funkcí, byly zde popsány i podmíněné výrazy (jedna z variant speciální formy), funkce pro manipulaci se seznamy a v neposlední řadě také funkce eval.
V průběhu dalších více než pěti desetiletí vzniklo mnoho dialektů tohoto programovacího jazyka, například MacLISP, InterLISP, ZetaLISP, XLisp, AutoLISP (původně odvozený z XLispu), Emacs LISP nebo slavný Common LISP. Kromě těchto implementací jazyka LISP, které se od sebe v několika ohledech odlišují (například existencí či neexistencí maker či objektového systému – CLOS v případě Common Lispu), vznikl v minulosti i nový dialekt tohoto jazyka nazvaný Scheme (původně Schemer), jehož autory jsou dnes již zmíněný Guy L. Steele a Gerald Jay Sussman.
Tento dialekt je implementačně jednodušší a také se ho lze naučit rychleji, než mnohé další varianty jazyka LISP. Právě z těchto důvodů se Scheme využívá či využívalo jak při výuce programování, tak i v mnoha open-source projektech, například v grafickém editoru GIMP jako jeden z podporovaných skriptovacích jazyků. Richard Stallman si dokonce přál, aby se Scheme stalo standardním skriptovacím jazykem většiny GNU aplikací, což je idea, která se především po vzniku dalších vysokoúrovňových programovacích jazyků (Perl, Python, TCL) nakonec neuskutečnila (z tohoto období pochází také známá TCL War, která ukázala některé konceptuální rozdíly mezi aplikacemi založenými na LISPu/Scheme a modelem UNIXu založeného na používání textových proudů).
Jednou z moderních reinkarnací LISPu je jazyk Clojure, jemuž se na stránkách Roota věnujeme poměrně intenzivně.
15. Literatura
- McCarthy
„Recursive functions of symbolic expressions and their computation by machine, part I“
1960 - Guy L. Steele
„History of Scheme“
2006, Sun Microsystems Laboratories - Kolář J., Muller K.:
„Speciální programovací jazyky“
Praha 1981 - „AutoLISP Release 9, Programmer's reference“
Autodesk Ltd., October 1987 - „AutoLISP Release 10, Programmer's reference“
Autodesk Ltd., September 1988 - McCarthy, John; Abrahams, Paul W.; Edwards, Daniel J.; Hart, Timothy P.; Levin, Michael I.
„LISP 1.5 Programmer's Manual“
MIT Press. ISBN 0 262 130 1 1 4 - Carl Hewitt; Peter Bishop and Richard Steiger
„A Universal Modular Actor Formalism for Artificial Intelligence“
1973 - Feiman, J.
„The Gartner Programming Language Survey (October 2001)“
Gartner Advisory
16. Odkazy na Internetu
- PicoLisp
http://picolisp.com/wiki/?home - A PicoLisp Tutorial
http://software-lab.de/doc/tut.html - Pico Lisp Documentation
http://picolisp.com/wiki/?Documentation - The PicoLisp Machine
http://software-lab.de/doc/ref.html#vm - Pico Lisp: A Case for Minimalist Interpreters?
http://lambda-the-ultimate.org/node/2124 - PicoLisp na Wikipedii
https://en.wikipedia.org/wiki/PicoLisp - Programovací jazyk LISP a LISP machines
http://www.root.cz/clanky/programovaci-jazyk-lisp-a-lisp-machines/ - Programovací jazyk LISP (druhá část)
http://www.root.cz/clanky/programovaci-jazyk-lisp-druha-cast/ - Steel Bank Common Lisp
http://www.sbcl.org/ - CLISP (implementace Common Lispu)
http://clisp.org/ - PLEAC-PicoLisp
http://pleac.sourceforge.net/pleac_picolisp/index.html#AEN4 - Rosetta Code – Category:Lisp
http://rosettacode.org/wiki/Category:Lisp - Emacs timeline
http://www.jwz.org/doc/emacs-timeline.html - EINE (Emacs Wiki)
http://www.emacswiki.org/emacs/EINE - EINE (Texteditors.org)
http://texteditors.org/cgi-bin/wiki.pl?EINE - ZWEI (Emacs Wiki)
http://www.emacswiki.org/emacs/ZWEI - ZWEI (Texteditors.org)
http://texteditors.org/cgi-bin/wiki.pl?ZWEI - Zmacs (Wikipedia)
https://en.wikipedia.org/wiki/Zmacs - Zmacs (Texteditors.org)
http://texteditors.org/cgi-bin/wiki.pl?Zmacs - TecoEmacs (Emacs Wiki)
http://www.emacswiki.org/emacs/TecoEmacs - Micro Emacs
http://www.emacswiki.org/emacs/MicroEmacs - Micro Emacs (Wikipedia)
https://en.wikipedia.org/wiki/MicroEMACS - EmacsHistory
http://www.emacswiki.org/emacs/EmacsHistory - Seznam editorů s ovládáním podobným Emacsu či kompatibilních s příkazy Emacsu
http://www.finseth.com/emacs.html