Obsah
1. Array programming a specializované programovací jazyky určené pro zpracování matic
2. Zpracování vektorů a matic v programovacím jazyku Kawa
3. Použití javovských polí v programovacím jazyku Kawa
4. Instrukce bajtkódu JVM určené pro konstrukci polí
9. Konstrukce N-rozměrných polí
10. Inicializace prvků N-rozměrných polí
11. Specifikace rozsahu (range)
12. Použití rozsahu (range) pro výběr hodnot z vektoru
13. Inicializace N-rozměrných polí s využitím rozsahů
14. Nepravidelná N-rozměrná pole
15. Malá odbočka na závěr: knihovny pro práci s vektory a maticemi pro programovací jazyk Clojure
17. Obsah následující části seriálu
18. Repositář s demonstračními příklady
1. Array programming a specializované programovací jazyky určené pro zpracování matic
Dnes se budeme zabývat jednou poměrně rozsáhlou oblastí v IT. Tou je zpracování vektorů, matic a taktéž vícerozměrných polí, protože s těmito strukturami se můžeme setkat v různých disciplínách, například ve finančnictví, pojišťovnictví, statistice, zpracování numerických dat, simulacích atd. Současně se jedná i o velmi zajímavou oblast, neboť právě kvůli nutnosti co nejrychlejší práce s velkými maticemi byly vytvořeny speciální výpočetní bloky v některých superpočítačích (příkladem mohou být superpočítače Cray) a došlo tak k důležitému podnětu pro další rozvoj výpočetní techniky (ten nepřímo vedl k vývoji moderních GPU). Současné knihovny pro práci s poli dokážou v případě potřeby využít jak některé rozšíření instrukčních sad (SIMD instrukce typu SSE neboli Streaming SIMD Extensions, původně též MMX či 3DNow!), tak i programovatelné grafické akcelerátory (GPU). SIMD instrukcemi jsme se již na stránkách Roota zabývali v samostatných článcích, zejména v:
- SIMD instrukce využívané v moderních mikroprocesorech řady x86
https://www.root.cz/clanky/simd-instrukce-vyuzivane-v-modernich-mikroprocesorech-rady-x86/ - SIMD instrukce v moderních mikroprocesorech řady x86 (2.část: SSE)
https://www.root.cz/clanky/simd-instrukce-v-modernich-mikroprocesorech-rady-x86–2-cast-sse/ - SIMD instrukce v moderních mikroprocesorech řady x86 (3.část: SSE2)
https://www.root.cz/clanky/simd-instrukce-v-modernich-mikroprocesorech-rady-x86–3-cast-sse2/
Práce s vektory a maticemi byla (a samozřejmě doposud je) podporována v překladačích FORTRANu, které začaly být po vzniku superpočítačů vybaveny specializovanými algoritmy, které dokázaly převést některé typy programových smyček na „vektorové operace“. Paralelně vznikly i specializované jazyky určené téměř výhradně pro práci s vektory i maticemi. Velmi dobrým příkladem jsou programovací jazyky APL a J. I těmito neobvyklými (ale stále používanými!) programovacími jazyky jsme se na stránkách Rootu již zabývali, a to v následujících článcích:
- Programování mainframů: jazyk APL
https://www.root.cz/clanky/programovani-mainframu-jazyk-apl/ - Programovací jazyk APL: programování bez smyček
https://www.root.cz/clanky/programovaci-jazyk-apl-programovani-bez-smycek/ - Programovací jazyk APL – dokončení
https://www.root.cz/clanky/programovaci-jazyk-apl-dokonceni/ - Programovací jazyk J – od hieroglyfů k ASCII znakům
https://www.root.cz/clanky/programovaci-jazyk-j-ndash-od-hieroglyfu-k-nbsp-ascii-znakum/ - Programujeme v jazyku J: vektory a matice
https://www.root.cz/clanky/programujeme-v-jazyku-j-ndash-vektory-a-matice/ - Programovací jazyk J: operátory, uživatelské funkce a tacit programming
https://www.root.cz/clanky/programovaci-jazyk-j-operatory-uzivatelske-funkce-a-tacit-programming/
Velmi dobrou podporu pro práci s maticemi ovšem nabízí i framework Torch založený na jazyku Lua, programovací jazyk Julia a knihovna Numpy určená pro programovací jazyk Python. Opět uvedu odkazy na články, v níž se touto populární a velmi často používanou knihovnou zabýváme do větší hloubky, než to umožňuje rozsah dnešního článku:
- Integrovaná vývojová prostředí ve Fedoře: vykreslování grafů s využitím knihoven Numpy a matplotlib
https://mojefedora.cz/integrovana-vyvojova-prostredi-ve-fedore-vykreslovani-grafu-s-vyuzitim-knihoven-numpy-a-matplotlib/ - Integrovaná vývojová prostředí ve Fedoře: praktické použití IPython Notebooku a knihovny Numpy (2.část)
https://mojefedora.cz/integrovana-vyvojova-prostredi-ve-fedore-prakticke-pouziti-ipython-notebooku-a-knihovny-numpy-2-cast/ - Integrovaná vývojová prostředí ve Fedoře: praktické použití IPython Notebooku a knihovny Numpy
https://mojefedora.cz/integrovana-vyvojova-prostredi-ve-fedore-prakticke-pouziti-ipython-notebooku-a-knihovny-numpy/
2. Zpracování vektorů a matic v programovacím jazyku Kawa
V dnešním článku se budeme zabývat především tím, jak se matice (prakticky libovolných rozměrů a s obecně více dimenzemi, tedy i vektory) používají v programovacím jazyku Kawa, kterému jsme se podrobně věnovali minule a předminule. Nutno říci, že v této oblasti je Kawa v poněkud schizofrenní situaci, protože pochopitelně podporuje klasické LISPovské vektory (nejedná se sice o zcela základní datový typ, ovšem prakticky každá implementace LISPu či Scheme práci s vektory umožňuje), dále podporuje N-dimenzionální pole (ND-array) vycházející z konceptů, které se objevily v jazyce Racket, konkrétně ve standardním modulu math-array a zapomenout nesmíme ani na možnost použít pole kompatibilní s programovacím jazykem Java a podporované přímo v bajtkódu JVM (viz též předchozí část tohoto seriálu).
Kromě toho si na konci článku připomeneme existenci knihovny nazvané core.matrix, která vývojářům pracujícím s programovacím jazykem Clojure nabízí prakticky všechny potřebné operace, které se při práci s vektory a maticemi používají (včetně například výpočtu inverzní matice). Navíc je tato knihovna zajímavá tím, že předepisuje rozhraní pro všechny operace, ovšem konkrétní implementaci je možné si vybrat. To například znamená, že pokud je řešen nějaký problém, v němž se ve velké míře používají takzvané řídké matice (pěkným příkladem z praxe může být Google matrix), lze – beze změny uživatelského programu – vybrat takovou reprezentaci matic a takové algoritmy, které jsou optimalizovány právě pro práci s řídkými maticemi a nikoli s maticemi uloženými ve formě dvourozměrného pole.
3. Použití javovských polí v programovacím jazyku Kawa
Nejprve si ukažme, jakým způsobem je možné v programovacím jazyku Kawa pracovat s poli plně kompatibilními s Javou a tím pádem samozřejmě i s virtuálním strojem Javy. Připomeňme si, že v Javě mají pole poměrně speciální postavení, protože se jedná o kontejner, který dokáže uložit známý počet prvků určitého typu (počet prvků musí být specifikován při konstrukci pole). Samozřejmě je možné, aby pole obsahovalo jako své prvky další pole, čímž je umožněno vytvářet matice i vícerozměrné datové struktury, které navíc nemusí mít nutně obdélníkový či čtvercový tvar. Interně je pole v operační paměti, přesněji řečeno na haldě (heap) uloženo v jediném souvislém bloku, ovšem musíme si přesně uvědomit co to znamená – pole, jehož prvky jsou primitivními datovými typy je skutečně tvořeno souvislým blokem, ovšem pole objektů je ve skutečnosti realizováno blokem obsahujícím reference, popř. speciální hodnotu null, zatímco vlastní objekty jsou alokovány na jiném místě haldy a pouze odkaz (reference) na ně je uložena v poli.
Vícerozměrná pole jsou vlastně „pole polí“ a tudíž pole obsahující reference (což je jeden z poměrně zásadních rozdílů mezi Javou a jazykem C v této oblasti).
Toto uspořádání přináší některé výhody, ale i nevýhody. Mezi výhody patří relativně snadná práce garbage collectoru při přenášení pole v rámci jednotlivých regionů, na něž je halda rozdělena (více viz a navazující články) a zmenšuje se i počet tzv. Monitorování procesů a správa paměti v JDK 6 a JDK 7 (2), ovšem počet objektů a spotřeba operační paměti poměrně rychle narůstá, protože velká část haldy může obsahovat pouze reference na objekty (extrémním příkladem může být rastrový obrázek, v němž jsou jednotlivé pixely realizovány instancemi třídy Color).
V programovacím jazyku Kawa je umožněno vytvářet klasická Javovská pole libovolného typu. Nejjednodušší je situace v případě, že se má jednat o pole s prvky primitivních datových typů. Vytvoření takového pole, zde konkrétně pole prvků typu int může vypadat následovně:
(define array1 (int[] length: 10)) (display array1) (newline)
S tímto výsledkem:
[0 0 0 0 0 0 0 0 0 0]
Samozřejmě je možné vytvořit funkci, která pole zkonstruuje a vrátí ho jako svoji návratovou hodnotu:
(define (createArray length) (int[] length: length)) (let ((array1 (createArray 10))) (display array1) (newline))
Výsledek:
[0 0 0 0 0 0 0 0 0 0]
Změna hodnoty prvku pole s využitím funkce set!:
(define array2 (int[] 1 2 3 4 5)) (display array2) (newline) (set! (array2 2) -1) (display array2) (newline)
S výsledkem:
[1 2 3 4 5] [1 2 -1 4 5]
Při přístupu k prvkům pole se hlídá rozsah indexů:
(define array2 (int[] 1 2 3 4 5)) (display array2) (newline) (set! (array2 100) -1) (display array2) (newline)
Při spuštění tohoto skriptu dojde k vyhození výjimky naprosto stejné, jako by tomu bylo v Javě:
[1 2 3 4 5] java.lang.ArrayIndexOutOfBoundsException: 100 at Array2_exception.run(Array2_exception.scm:4) at gnu.expr.ModuleExp.evalModule2(ModuleExp.java:289) at gnu.expr.CompiledModule.evalModule(CompiledModule.java:42) at gnu.expr.CompiledModule.evalModule(CompiledModule.java:60) at kawa.Shell.runFile(Shell.java:565) at kawa.Shell.runFileOrClass(Shell.java:468) at kawa.repl.processArgs(repl.java:700) at kawa.repl.main(repl.java:820)
4. Instrukce bajtkódu JVM určené pro konstrukci polí
Konstrukce pole a přístup k prvkům pole je plně podporován i bajtkódem virtuálního stroje Javy. Zatímco objekty se vytváří s využitím instrukce new a pro přístup k jejich atributům se používají instrukce getfield a putfield, je situace v případě polí odlišná, protože se pro pole používá dvacet specializovaných instrukcí sloužících jak pro vytvoření pole, tak i pro přístup k jeho prvkům, popř. pro zjištění délky pole.
První instrukcí, se kterou se dnes seznámíme, je instrukce nazvaná newarray. Pravděpodobně uhodnete, k čemu tato instrukce slouží – lze ji použít pro vytvoření pole dané délky, ovšem pouze v tom případě, pokud má pole obsahovat prvky některého z primitivních datových typů, tj. pravdivostní hodnoty, znaky, celá čísla či čísla s plovoucí řádovou čárkou. Jinými slovy to znamená, že tuto instrukci nelze použít například pro vytvoření pole objektů. Formát instrukce newarray je vypsán v následující tabulce:
# | Instrukce | Opkód | Operandy | Prováděná operace |
---|---|---|---|---|
1 | newarray | 0×BC | arraytype | Vytvoří nové pole s prvky primitivního datového typu |
Instrukce newarray očekává, že na vrcholu zásobníku operandů (TOS) bude uložena hodnota typu int udávající velikost pole. Tato hodnota je ze zásobníku v průběhu vytváření pole odstraněna a namísto ní se na vrchol zásobníku operandů uloží reference na právě vytvořené pole. Ještě nám zbývá popsat operand instrukce newarray nazvaný arraytype. Jde o jednobajtový operand, jehož hodnota určuje typ prvků vytvářeného pole. Jak jsme si již řekli v předchozím odstavci, může se pomocí instrukce newarray vytvořit pole složené z pravdivostních hodnot, znaků, celých čísel či čísel s plovoucí řádovou čárkou (ve všech případech se jedná o primitivní datové typy):
Arraytype | Typ prvků pole |
---|---|
4 | boolean |
5 | char |
6 | float |
7 | double |
8 | byte |
9 | short |
10 | int |
11 | long |
Vytvoření pole pomocí:
(int[] length: 10)
Se do bajtkódu přeloží takto:
5: bipush 10 7: newarray int
Naproti tomu funkce pro alokaci pole:
(define (createArray length) (int[] length: length))
Je přeložena do bajtkódu následujícím (neefektivním) způsobem:
public static int[] createArray(java.lang.Object); Code: 0: aload_0 1: invokestatic #16 // Method gnu/mapping/Promise.force:(Ljava/lang/Object;)Ljava/lang/Object; 4: checkcast #18 // class java/lang/Number 7: invokevirtual #22 // Method java/lang/Number.intValue:()I 10: newarray int 12: areturn
Lepší je použít typovou informaci o parametru length:
(define (createArray length :: int) (int[] length: length)) (let ((array1 (createArray 10))) (display array1) (newline))
Nyní je překlad funkce createArray do bajtkódu velmi efektivní:
public static int[] createArray(int); Code: 0: iload_0 1: newarray int 3: areturn
Další instrukcí používanou při vytváření polí je instrukce nazvaná anewarray (na začátku jména této instrukce se nachází znak „a“). Tato instrukce se používá pro vytvoření pole, jehož prvky jsou objekty, a to objekty libovolného (specifikovaného) typu. Zatímco se u instrukce newarray specifikoval typ prvků pole pomocí hodnoty bajtu uloženého ihned za operačním kódem instrukce, je nutné u instrukce anewarray použít celé jméno třídy, rozhraní či výčtového typu (udávající typ prvků pole). Toto jméno třídy je, jak pravděpodobně již tušíte, uložené v constant poolu, takže se za operačním kódem instrukce anewarray nachází dvojice bajtů představujících index záznamu v constant poolu.
Chování obou zmíněných instrukcí však zůstává stejné – z vrcholu zásobníku operandů se vyzvedne hodnota typu int představující velikost pole, pole daného typu se vytvoří a následně se na zásobník operandů uloží reference na vytvořený objekt (v případě polí objektů samozřejmě nejsou tyto objekty vytvořeny a pole obsahuje hodnoty null):
# | Instrukce | Opkód | Operandy | Prováděná operace |
---|---|---|---|---|
1 | anewarray | 0×BD | highbyte, lowbyte | Vytvoří nové pole objektů |
Příklad použití:
(define (createStringArray length :: int) (String[] length: length)) (let ((array1 (createStringArray 10))) (display array1) (newline))
Překlad do bajtkódu:
public static java.lang.String[] createStringArray(int); Code: 0: iload_0 1: anewarray #10 // class java/lang/String 4: areturn
5. Inicializace prvků polí
Pole je možné při jejich konstrukci přímo i inicializovat, což je pochopitelně podporováno i v programovacím jazyku Kawa. Konstrukce pole s inicializací jeho prvků vypadá následovně:
(define array2 (int[] 1 2 3 4 5)) (display array2) (newline) (set! (array2 2) -1) (display array2) (newline)
S výsledkem:
[1 2 3 4 5] [1 2 -1 4 5]
Ukažme si nyní nepatrně složitější příklad, v němž mají prvky hodnoty, které se nepodobají indexům prvků (což by nás mátlo při studiu bajtkódu):
(define array2 (int[] 100 200 300 400 500)) (set! (array2 2) -1)
Jak se tato zdánlivě triviální konstrukce přeloží do bajtkódu? Ukazuje se, že nepříliš efektivně – v bajtkódu se nejdříve pole zkonstruuje a následně se jednotlivé prvky inicializují samostatně, vždy několika instrukcemi (iconst+*push+iastore) pro každý prvek:
public final void run(gnu.mapping.CallContext); Code: 0: aload_1 1: getfield #8 // Field gnu/mapping/CallContext.consumer:Lgnu/lists/Consumer; 4: astore_2 5: iconst_5 6: newarray int 8: dup 9: iconst_0 10: bipush 100 12: iastore 13: dup 14: iconst_1 15: sipush 200 18: iastore 19: dup 20: iconst_2 21: sipush 300 24: iastore 25: dup 26: iconst_3 27: sipush 400 30: iastore 31: dup 32: iconst_4 33: sipush 500 36: iastore 37: putstatic #12 // Field array2:[I 40: getstatic #12 // Field array2:[I 43: iconst_2 44: iconst_m1 45: iastore 46: return
Podobně lze zkonstruovat a inicializovat i pole prvků typu float:
(define array3 (float[] 100 200 300 400 500 600 700 800 900 1000)) (display array3) (newline) (set! (array3 9) -1000) (display array3) (newline)
S výsledkem:
[100.0 200.0 300.0 400.0 500.0 600.0 700.0 800.0 900.0 1000.0] [100.0 200.0 300.0 400.0 500.0 600.0 700.0 800.0 900.0 -1000.0]
Překlad do bajtkódu JVM:
5: bipush 10 7: newarray float 9: dup 10: iconst_0 11: bipush 100 13: i2f 14: fastore 15: dup 16: iconst_1 17: ldc #9 // float 200.0f 19: fastore 20: dup 21: iconst_2 22: ldc #10 // float 300.0f 24: fastore 25: dup 26: iconst_3 27: ldc #11 // float 400.0f 29: fastore 30: dup 31: iconst_4 32: ldc #12 // float 500.0f 34: fastore 35: dup 36: iconst_5 37: ldc #13 // float 600.0f 39: fastore 40: dup 41: bipush 6 43: ldc #14 // float 700.0f 45: fastore 46: dup 47: bipush 7 49: ldc #15 // float 800.0f 51: fastore 52: dup 53: bipush 8 55: ldc #16 // float 900.0f 57: fastore 58: dup 59: bipush 9 61: ldc #17 // float 1000.0f 63: fastore
6. Vícerozměrná javovská pole
V předchozím textu jsme si ukázali, jakým způsobem je možné pracovat s jednorozměrnými javovskými poli libovolného typu. Ovšem programovací jazyk Kawa pochopitelně podporuje i vícerozměrná pole, která je možné zkonstruovat jediným příkazem, bez nutnosti konstruovat pole nižších dimenzí. Celou operaci si pochopitelně ukážeme na několika demonstračních příkladech. Nejprve vytvoření dvourozměrného pole:
(define matrix1 (int[][] [1 2 3] [4 5 6] [7 8 9])) (display matrix1) (newline)
Po spuštění tohoto příkladu by se na standardním výstupu měl objevit obsah matice:
[[1 2 3] [4 5 6] [7 8 9]]
Pole obsahující jako své prvky další pole, ovšem s různými délkami:
(define matrix2 (int[][] [1] [2 3] [4 5 6] [7 8 9 10])) (display matrix2) (newline)
Výsledkem bude:
[[1] [2 3] [4 5 6] [7 8 9 10]]
Totéž, ovšem pro prvky typu float:
(define matrix3 (float[][] [1] [2 3] [4 5 6] [7 8 9 10])) (display matrix3) (newline)
Výsledkem v tomto případě bude:
[[1.0] [2.0 3.0] [4.0 5.0 6.0] [7.0 8.0 9.0 10.0]]
Trojrozměrné pole:
(int[][][] [[1 2] [3 4]] [[5 6] [7 8]])
7. Vektory
Javovská pole podporovaná v programovacím jazyku Kawa přináší několik výhod, ale pochopitelně i nevýhod. Samotná pole mají pevnou délku a jejich prvky jsou vždy stejného typu. Tato vlastnost (výhoda a nevýhoda současně) umožňuje velmi efektivní přístup k prvkům pole, který má u jednorozměrných polí konstantní složitost. Současně jsme však omezeni například tím, že do javovských polí není možné jednoduše ukládat zlomky, celá čísla s libovolným rozsahem atd. V případě, že budeme potřebovat i tuto funkcionalitu, je nutné namísto polí použít odlišné datové typy programovacího jazyka Kawa. Může se jednat o vektory podporované v mnoha implementacích LISPu i Scheme (a taktéž v jazyku Clojure, i když zde mají vektory zcela odlišné vnitřní uspořádání) nebo o typ pojmenovaný pro větší zmatek v terminologii array.
V této kapitole si ukážeme práci s takzvanými vektory.
# | Funkce | Stručný popis funkce |
---|---|---|
1 | vector | konstrukce vektoru a inicializace jeho prvků |
2 | vector-ref | přístup k prvku vektoru |
3 | vector-set! | změna hodnoty prvku vektoru |
4 | vector? | predikát: dotaz, zda je předaná hodnota typu vektor či nikoli |
5 | vector-length | vrací délku vektoru, tedy počet jeho prvků |
6 | vector->list | převod vektoru na seznam |
7 | list->vector | opačný převod |
Vektor lze zkonstruovat speciálním „konstruktorem“, v němž se jednotlivé prvky vektoru zapisují do hranatých závorek (což již známe z programovacího jazyka Clojure). K prvkům vektoru se přistupuje funkcí vector-ref:
(define vector1 [1 2 3 4]) (display vector1) (newline) (display (vector-ref vector1 0)) (display (vector-ref vector1 10))
Výsledek:
#(1 2 3 4) java.lang.ArrayIndexOutOfBoundsException: 10 at gnu.lists.FVector.get(FVector.java:105) at kawa.lib.vectors.vectorRef(vectors.scm:21) at Vectors1.run(Vectors1.scm:7) at gnu.expr.ModuleExp.evalModule2(ModuleExp.java:289) at gnu.expr.CompiledModule.evalModule(CompiledModule.java:42) at gnu.expr.CompiledModule.evalModule(CompiledModule.java:60) at kawa.Shell.runFile(Shell.java:565) at kawa.Shell.runFileOrClass(Shell.java:468) at kawa.repl.processArgs(repl.java:700) at kawa.repl.main(repl.java:820)
Vektory lze taktéž zapsat stylem #(), který je kompatibilní s R7RS:
#|kawa:1|# #(1 2 3) #(1 2 3) #|kawa:2|# [1 2 3] #(1 2 3) #|kawa:3|# (eq? #(1 2 3) [1 2 3]) #f #|kawa:4|# (equal? #(1 2 3) [1 2 3]) #t
V dalším příkladu je namísto zápisu prvků vektoru do hranatých závorek použit konstruktor představovaný funkcí nazvanou jednoduše vector. Taktéž je zde ukázána změna hodnoty vybraného prvku s využitím funkce vector-set! (tato funkce opět obsahuje ve svém jménu vykřičník, protože mění stav aplikace):
(define vector2 (vector 1 2 3 4 5)) (display vector2) (newline) (display (vector-ref vector2 0)) (newline) (vector-set! vector2 2 -1) (display vector2) (newline)
Výsledek:
#(1 2 3 4 5) 1 #(1 2 -1 4 5)
Dotazy, zda je daná hodnota vektorem či nikoli, používají predikát vector?:
#|kawa:25|# (vector? "A") #f #|kawa:26|# (vector? [1 2 3]) #t #|kawa:27|# (vector? '(1 2 3)) #f
Další funkce je určena pro získání velikosti vektoru, tedy počtu jeho prvků:
#|kawa:28|# (vector-length [1 2 3]) 3 #|kawa:29|# (vector-length []) 0
8. N-rozměrná pole (ND-Array)
V navazujících kapitolách se seznámíme s možnostmi typu array, což je datový typ představující N-rozměrná pole. Kromě toho si ukážeme i práci s takzvanými „rozsahy“ (range), které do značné míry s poli souvisí.
Datový typ array se používá nejenom v jazyce Kawa, ale například i v programovacím jazyce Racket, s nímž se seznámíme v navazujících částech tohoto seriálu (jedná se pravděpodobně o nejrozsáhlejší a nejúplnější implementaci Scheme vůbec). Samotné pole se skládá ze dvou částí: hodnot jednotlivých prvků a tvaru pole neboli shape. Tvar pole je důležitou strukturou, protože (nepřímo) určuje, jakým způsobem jsou prvky v poli uspořádány. To však není vše, protože je možné jednoduše tvar pole změnit a tím pádem prvky zdánlivě zpřeházet (interně se ovšem v operační paměti s prvky v některých případech manipulovat nemusí). Další důležitou vlastností datového typu array je možnost uložit do pole libovolné hodnoty; jedná se tedy o heterogenní kontejner, na rozdíl od běžných javovských polí.
9. Konstrukce N-rozměrných polí
Ke konstrukci N-rozměrného pole slouží funkce nazvaná make-array. Této funkci se předává vektor obsahující velikosti (rozsah indexů) N-rozměrného pole ve všech dimenzích. Počet prvků tohoto vektoru tedy odpovídá počtu dimenzí. Dále je možné této funkci předat i hodnoty jednotlivých prvků, což si ukážeme v navazující kapitole. Funkci make-array si můžeme velmi snadno otestovat v interaktivní smyčce REPL programovacího jazyka Kawa.
Mezní případ – prázdné pole:
#|kawa:3|# (make-array [0]) #()
Konstrukce jednoprvkového jednorozměrné pole:
#|kawa:8|# (make-array [1]) #(#!null)
Desetiprvkový vektor:
#|kawa:5|# (make-array [10]) #(#!null #!null #!null #!null #!null #!null #!null #!null #!null #!null)
Konstrukce matice 1×1 s jediným prvkem:
#|kawa:9|# (make-array [1 1]) ╔#2a:1:1 ║#!null║ ╚══════╝
Konstrukce matice s jedním řádkem a dvěma sloupci:
#|kawa:7|# (make-array [1 2]) ╔#2a:1:2══════╗ ║#!null│#!null║ ╚══════╧══════╝
Konstrukce matice se dvěma řádky a třemi sloupci:
#|kawa:10|# (make-array [2 3]) ╔#2a:2:3══════╤══════╗ ║#!null│#!null│#!null║ ╟──────┼──────┼──────╢ ║#!null│#!null│#!null║ ╚══════╧══════╧══════╝
Trojrozměrná struktura 2×3×4 prvky:
#|kawa:11|# (make-array [2 3 4]) ╔#3a:2:3:4════╤══════╤══════╗ ║#!null│#!null│#!null│#!null║ ╟──────┼──────┼──────┼──────╢ ║#!null│#!null│#!null│#!null║ ╟──────┼──────┼──────┼──────╢ ║#!null│#!null│#!null│#!null║ ╠══════╪══════╪══════╪══════╣ ║#!null│#!null│#!null│#!null║ ╟──────┼──────┼──────┼──────╢ ║#!null│#!null│#!null│#!null║ ╟──────┼──────┼──────┼──────╢ ║#!null│#!null│#!null│#!null║ ╚══════╧══════╧══════╧══════╝
Taktéž trojrozměrná struktura, ovšem tentokrát s tvarem 4×3×2 prvky:
#|kawa:12|# (make-array [4 3 2]) ╔#3a:4:3:2════╗ ║#!null│#!null║ ╟──────┼──────╢ ║#!null│#!null║ ╟──────┼──────╢ ║#!null│#!null║ ╠══════╪══════╣ ║#!null│#!null║ ╟──────┼──────╢ ║#!null│#!null║ ╟──────┼──────╢ ║#!null│#!null║ ╠══════╪══════╣ ║#!null│#!null║ ╟──────┼──────╢ ║#!null│#!null║ ╟──────┼──────╢ ║#!null│#!null║ ╠══════╪══════╣ ║#!null│#!null║ ╟──────┼──────╢ ║#!null│#!null║ ╟──────┼──────╢ ║#!null│#!null║ ╚══════╧══════╝
Čtyřrozměrné pole:
#|kawa:7|# (make-array [2 2 2 2]) ╔#4a:2:2:2:2══╗ ║#!null│#!null║ ╟──────┼──────╢ ║#!null│#!null║ ╠══════╪══════╣ ║#!null│#!null║ ╟──────┼──────╢ ║#!null│#!null║ ╠══════╪══════╣ ║#!null│#!null║ ╟──────┼──────╢ ║#!null│#!null║ ╠══════╪══════╣ ║#!null│#!null║ ╟──────┼──────╢ ║#!null│#!null║ ╚══════╧══════╝
Pole, které má v jedné dimenzi nulovou velikost a celkově tedy nula prvků:
#|kawa:11|# (make-array [0 2 2 2]) #4a:0:2:2:2 ()
10. Inicializace prvků N-rozměrných polí
Funkci make-arrray, s jejím základním použitím jsme se seznámili v předchozí kapitole, je možné předat i hodnoty jednotlivých prvků vytvářeného pole. Pokud je počet zadaných hodnot menší než počet prvků, budou se prvky opakovat tak dlouho, až se pole postupně vyplní. Samozřejmě se opět podíváme na příklady.
Vektor obsahující stejné hodnoty ve všech prvcích:
#|kawa:13|# (make-array [10] 1/2) #(1/2 1/2 1/2 1/2 1/2 1/2 1/2 1/2 1/2 1/2)
Dvourozměrné pole (matice) se dvěma řádky a čtyřmi sloupci:
#|kawa:1|# (make-array [2 4] 1 2 3 4 5) ╔#2a:2:4╗ ║1│2│3│4║ ╟─┼─┼─┼─╢ ║5│1│2│3║ ╚═╧═╧═╧═╝
Pole 5×5 prvků se shodnými řádky:
#|kawa:14|# (make-array [5 5] 1 2 3 4 5) ╔#2a:5:5╤═╗ ║1│2│3│4│5║ ╟─┼─┼─┼─┼─╢ ║1│2│3│4│5║ ╟─┼─┼─┼─┼─╢ ║1│2│3│4│5║ ╟─┼─┼─┼─┼─╢ ║1│2│3│4│5║ ╟─┼─┼─┼─┼─╢ ║1│2│3│4│5║ ╚═╧═╧═╧═╧═╝
Trojrozměrné pole 2×3×4 prvky:
#|kawa:2|# (make-array [2 3 4] 1 2 3 4 5) #3a:2:3:4 ║1│2│3│4║ ╟─┼─┼─┼─╢ ║5│1│2│3║ ╟─┼─┼─┼─╢ ║4│5│1│2║ ╠═╪═╪═╪═╣ ║3│4│5│1║ ╟─┼─┼─┼─╢ ║2│3│4│5║ ╟─┼─┼─┼─╢ ║1│2│3│4║ ╚═╧═╧═╧═╝
Čtyřrozměrné pole 2×2×2×3 prvky:
#|kawa:11|# (make-array [2 2 2 3] 1 2 3) #4a═╤═╗ ║1│2│3║ ╟─┼─┼─╢ ║1│2│3║ ╠═╪═╪═╣ ║1│2│3║ ╟─┼─┼─╢ ║1│2│3║ ╠═╪═╪═╣ ║1│2│3║ ╟─┼─┼─╢ ║1│2│3║ ╠═╪═╪═╣ ║1│2│3║ ╟─┼─┼─╢ ║1│2│3║ ╚═╧═╧═╝
Pole obsahující symboly:
#|kawa:23|# (make-array [3 3 3] 'x 'y 'z) #3a═╤═╗ ║x│y│z║ ╟─┼─┼─╢ ║x│y│z║ ╟─┼─┼─╢ ║x│y│z║ ╠═╪═╪═╣ ║x│y│z║ ╟─┼─┼─╢ ║x│y│z║ ╟─┼─┼─╢ ║x│y│z║ ╠═╪═╪═╣ ║x│y│z║ ╟─┼─┼─╢ ║x│y│z║ ╟─┼─┼─╢ ║x│y│z║ ╚═╧═╧═╝
11. Specifikace rozsahu (range)
V jedenácté kapitole se seznámíme s velmi užitečným konceptem takzvaných rozsahů neboli range. Jedná se o jeden ze způsobů, jakým lze v programovacím jazyku Kawa popsat sekvenci hodnot bez toho, aby bylo nutné explicitně vypsat všechny prvky v sekvenci (a navíc může být zápis názornější, než v případě použití funkce range známé z mnoha jiných programovacích jazyků). Nejnázornější bude si ukázat možnosti, které při specifikaci rozsahů máme.
Hodnoty od 1 do 9 (hodnota 10 již v rozsahu není):
#|kawa:1|# [1 <: 10] #(1 2 3 4 5 6 7 8 9)
Hodnoty od 1 do 10, včetně obou mezí:
#|kawa:2|# [1 <=: 10] #(1 2 3 4 5 6 7 8 9 10)
Počítání směrem k záporné ose (bez uvedení kroku):
#|kawa:3|# [10 >: 0] #(10 9 8 7 6 5 4 3 2 1)
Dtto, ale včetně nuly:
#|kawa:5|# [10 >=: 0] #(10 9 8 7 6 5 4 3 2 1 0)
Specifikace kroku:
#|kawa:4|# [10 by: -2 >: 0] #(10 8 6 4 2)
Dtto, ale včetně nuly:
#|kawa:6|# [10 by: -2 >=: 0] #(10 8 6 4 2 0)
Práce se zlomky:
#|kawa:7|# [1 by: 1/2 <=: 10] #(1 3/2 2 5/2 3 7/2 4 9/2 5 11/2 6 13/2 7 15/2 8 17/2 9 19/2 10)
Počítání po 1/10 (což v IEEE 754 není možné):
#|kawa:8|# [0 by: 1/10 <=: 1] #(0 1/10 1/5 3/10 2/5 1/2 3/5 7/10 4/5 9/10 1)
Výsledkem bude prázdný vektor:
#|kawa:14|# [0 by: 1 <=: -1] #()
12. Použití rozsahu (range) pro výběr hodnot z vektoru
Rozsahy je možné použít i pro indexaci (výběr) většího množství hodnot z vektoru či z jiné datové struktury podporující indexaci. Ukážeme si to na příkladu řetězce obsahujícího všechny znaky malé abecedy:
#|kawa:17|# (define abeceda "abcdefghijklmnopqrstuvwxyz")
Výběr pátého až desátého znaku:
#|kawa:18|# (abeceda [5 <=: 10]) fghijk
Výběr pátého až desátého znaku, ovšem s přeskočením sudých znaků:
#|kawa:19|# (abeceda [5 by: 2 <=: 10]) fhj
Výběr znaků pozpátku:
#|kawa:20|# (abeceda [20 >=: 5]) utsrqponmlkjihgf
Celá abeceda, ovšem vybraná pozpátku:
#|kawa:21|# (abeceda [25 >=: 0]) zyxwvutsrqponmlkjihgfedcba
Celá abeceda:
#|kawa:22|# (abeceda [<:]) abcdefghijklmnopqrstuvwxyz
13. Inicializace N-rozměrných polí s využitím rozsahů
Rozsahy popsané v předchozí kapitole nám umožňují vytvořit pole s libovolným počtem rozměrů, ve kterých se budou vyskytovat sekvence hodnot. V tomto případě použijeme funkci index-array umožňující inicializaci pole takovým způsobem, že každý prvek bude obsahovat svůj index:
#|kawa:2|# (index-array [[1 <: 10]]) ╔#1a@1:9╤═╤═╤═╤═╤═╗ ║0│1│2│3│4│5│6│7│8║ ╚═╧═╧═╧═╧═╧═╧═╧═╧═╝
Popř.:
#|kawa:3|# (index-array [[1 <=: 10]]) ╔#1a@1:10═╤═╤═╤═╤═╤═╗ ║0│1│2│3│4│5│6│7│8│9║ ╚═╧═╧═╧═╧═╧═╧═╧═╧═╧═╝
Dvourozměrné pole:
#|kawa:3|# (index-array [[1 <: 3] [2 <: 6]]) #2a@1:2@2:4 ║0│1│2│3║ ╟─┼─┼─┼─╢ ║4│5│6│7║ ╚═╧═╧═╧═╝
Trojrozměrné pole:
#|kawa:5|# (index-array [[1 <: 4] [1 <: 4] [1 <: 4]]) #3a@1:3@1:3@1:3 ║ 0│ 1│ 2║ ╟──┼──┼──╢ ║ 3│ 4│ 5║ ╟──┼──┼──╢ ║ 6│ 7│ 8║ ╠══╪══╪══╣ ║ 9│10│11║ ╟──┼──┼──╢ ║12│13│14║ ╟──┼──┼──╢ ║15│16│17║ ╠══╪══╪══╣ ║18│19│20║ ╟──┼──┼──╢ ║21│22│23║ ╟──┼──┼──╢ ║24│25│26║ ╚══╧══╧══╝
Odlišný spodní index:
#|kawa:12|# (index-array [[3 <: 7] [3 <: 7] [3 <: 7]]) #3a@3:4@3:4@3:4 ║ 0│ 1│ 2│ 3║ ╟──┼──┼──┼──╢ ║ 4│ 5│ 6│ 7║ ╟──┼──┼──┼──╢ ║ 8│ 9│10│11║ ╟──┼──┼──┼──╢ ║12│13│14│15║ ╠══╪══╪══╪══╣ ║16│17│18│19║ ╟──┼──┼──┼──╢ ║20│21│22│23║ ╟──┼──┼──┼──╢ ║24│25│26│27║ ╟──┼──┼──┼──╢ ║28│29│30│31║ ╠══╪══╪══╪══╣ ║32│33│34│35║ ╟──┼──┼──┼──╢ ║36│37│38│39║ ╟──┼──┼──┼──╢ ║40│41│42│43║ ╟──┼──┼──┼──╢ ║44│45│46│47║ ╠══╪══╪══╪══╣ ║48│49│50│51║ ╟──┼──┼──┼──╢ ║52│53│54│55║ ╟──┼──┼──┼──╢ ║56│57│58│59║ ╟──┼──┼──┼──╢ ║60│61│62│63║ ╚══╧══╧══╧══╝
14. Nepravidelná N-rozměrná pole
V programovacím jazyku Kawa mohou být prvky N-rozměrných polí libovolného typu. Může se jednat i o další pole atd. Ukažme si tuto možnost na několika demonstračních příkladech.
(make-array [2 3] (make-array [2 2] 1/2) 42 "foobar" [1 2 3 4] (make-array [4 1] 0) (make-array [5 5] 'x)) ╔#2a:2:3══╤═══╤═════════════╗ ║╔#2a:2:2╗│ 42│╔#1a:6╤═╤═╤═╗║ ║║1/2│1/2║│ │║f│o│o│b│a│r║║ ║╟───┼───╢│ │╚═╧═╧═╧═╧═╧═╝║ ║║1/2│1/2║│ │ ║ ║╚═══╧═══╝│ │ ║ ╟─────────┼───┼─────────────╢ ║╔#1a:4╤═╗│#2a│╔#2a:5:5╤═╗ ║ ║║1│2│3│4║│║0║│║x│x│x│x│x║ ║ ║╚═╧═╧═╧═╝│╟─╢│╟─┼─┼─┼─┼─╢ ║ ║ │║0║│║x│x│x│x│x║ ║ ║ │╟─╢│╟─┼─┼─┼─┼─╢ ║ ║ │║0║│║x│x│x│x│x║ ║ ║ │╟─╢│╟─┼─┼─┼─┼─╢ ║ ║ │║0║│║x│x│x│x│x║ ║ ║ │╚═╝│╟─┼─┼─┼─┼─╢ ║ ║ │ │║x│x│x│x│x║ ║ ║ │ │╚═╧═╧═╧═╧═╝ ║ ╚═════════╧═══╧═════════════╝
V dalším příkladu je vytvořena matice se dvěma řádky a třemi sloupci. Prvky této matice jsou další matice (2×2 prvky atd.) i číselné hodnoty nebo vektory:
#|kawa:52|# (array [2 3] #2a((1 2) (3 4)) 9 #2a((3 4) (5 6)) [42 43] #2a:1:3((8 7 6)) #2a((90 91) (100 101))) ╔#2a:2:3╤═══════╤═════════╗ ║#2a═╗ │ 9│#2a═╗ ║ ║║1│2║ │ │║3│4║ ║ ║╟─┼─╢ │ │╟─┼─╢ ║ ║║3│4║ │ │║5│6║ ║ ║╚═╧═╝ │ │╚═╧═╝ ║ ╟───────┼───────┼─────────╢ ║╔#1a:2╗│#2a:1:3│╔#2a:2:2╗║ ║║42│43║│║8│7│6║│║ 90│ 91║║ ║╚══╧══╝│╚═╧═╧═╝│╟───┼───╢║ ║ │ │║100│101║║ ║ │ │╚═══╧═══╝║ ╚═══════╧═══════╧═════════╝
Následující příklad byl převzat z oficiální dokumentace a byl pouze nepatrně upraven pro větší čitelnost:
#|kawa:17|# (array [[1 <=: 2] [1 <=: 3]] #2a((1 2) (3 4)) 9 #2a((3 4) (5 6)) [42 43] #2a:1:3((8 7 6)) #2a((90 91) (100 101))) ╔#2a@1:2@1:3════╤═════════╗ ║#2a═╗ │ 9│#2a═╗ ║ ║║1│2║ │ │║3│4║ ║ ║╟─┼─╢ │ │╟─┼─╢ ║ ║║3│4║ │ │║5│6║ ║ ║╚═╧═╝ │ │╚═╧═╝ ║ ╟───────┼───────┼─────────╢ ║╔#1a:2╗│#2a:1:3│╔#2a:2:2╗║ ║║42│43║│║8│7│6║│║ 90│ 91║║ ║╚══╧══╝│╚═╧═╧═╝│╟───┼───╢║ ║ │ │║100│101║║ ║ │ │╚═══╧═══╝║ ╚═══════╧═══════╧═════════╝
15. Malá odbočka na závěr: knihovny pro práci s vektory a maticemi pro programovací jazyk Clojure
Pojďme si nyní alespoň ve stručnosti připomenout, jakým způsobem je práce s maticemi podporována v programovacím jazyku Clojure, protože Clojure do určité míry obsazuje stejný segment, jako dnes popisovaný programovací jazyk Kawa. Při studiu základních knihoven Clojure je možné dojít k závěru, že vlastně jen velmi málo funkcí a maker je určeno pro práci s těmito datovými typy, i když je samozřejmě možné jak vektory, tak i matice velmi snadno reprezentovat s využitím základních sekvenčních datových struktur Clojure – seznamů a vektorů. Ve skutečnosti to však není zcela ideální řešení, a to hned z několika důvodů, jejichž společným rysem je rychlost prováděných operací a do určité míry i nároky na operační paměť.
Z tohoto důvodu je v případě implementace algoritmů, v nichž se intenzivně používají operace s maticemi, mnohem výhodnější využít možností nabízených specializovanými knihovnami. My se dnes seznámíme především s elegantně navrženou knihovnou core.matrix. Existují ovšem ještě výkonnější řešení: knihovna Neanderthal, která využívá vysoce optimalizovanou nativní knihovnu ATLAS (Automatically Tuned Linear Algebra Software) s možností využití vysokého výpočetního výkonu současných GPU.
V přednášce nazvané velmi příhodně „Enter the Matrix“, která je dostupná na adrese http://www.slideshare.net/mikeranderson/2013–1114-enter-thematrix, je mj. ukázáno, jakým způsobem jsou v Clojure implementována různá paradigmata programování. Díky podpoře maker a způsobu zápisu programového kódu v Clojure lze velmi snadno implementovat různé doménově specifické jazyky (DSL), mj. i právě jazyk pro array programming (viz též úvodní kapitolu dnešního článku):
Paradigma | Jazyk | Implementace v Clojure |
---|---|---|
funkcionální | Haskell | clojure.core |
OOP | Smalltalk | clojure.core |
metaprogramování | Lisp | clojure.core |
logické | Prolog | core.logic |
array programming | APL, J | core.matrix |
16. Knihovna core.matrix
V dalším textu se budeme zabývat knihovnou nazvanou core.matrix, která je určena těm vývojářům, kteří ve svých projektech potřebují provádět velké množství operací s těmito strukturami, a to na poměrně vysoké úrovni, tj. bez nutnosti přesně specifikovat, jak mají být matice uloženy v paměti, jakým způsobem provádět operaci násobení matic atd. Díky tomuto přístupu a taktéž díky vlastnostem programovacího jazyka Clojure (existence tzv. threading makra a funkcí vyššího řádu) se práce s maticemi do značné míry začíná podobat práci v APL, až na ten rozdíl, že algoritmy zapisované v Clojure jsou pro většinu vývojářů přece jen čitelnější :-). Důležité je, že rozhraní definované v knihovně core.matrix může mít několik implementací. V současnosti se jedná o vectorz-clj, Clatrix a NDArray. V core.matrix navíc došlo k rozšíření operátorů +, – atd. takovým způsobem, že je lze použít i pro zpracování vektorů a matic (ve skutečnosti se samozřejmě nejedná o skutečné operátory, protože tento koncept Clojure a vlastně ani žádný další lispovský jazyk nepotřebuje).
Funkce a makra nabízená knihovnou core.matrix nejlépe prozkoumáme přímo s využitím REPLu, tj. interaktivního rozhraní, v němž ihned po zadání dochází k expanzi maker a vyhodnocování funkcí:
Konstrukce vektorů a matic
; vektor matrixtest.core=> (matrix [1 2 3]) [1 2 3] ; vektor matrixtest.core=> (matrix '(1 2 3)) [1 2 3] ; matice matrixtest.core=> (matrix [[1 2] [3 4]]) [[1 2] [3 4]] ; matice matrixtest.core=> (matrix (range 1 10)) [1 2 3 4 5 6 7 8 9] ; matice matrixtest.core=> (matrix [[1 2 3] [4 5 6] [7 8 9]]) [[1 2 3] [4 5 6] [7 8 9]]
Pretty printing matic a vektorů
matrixtest.core=> (pm (matrix [[1 2] [3 4]])) [[1.000 2.000] [3.000 4.000]] matrixtest.core=> (matrix [[1 2] [3 4]]) [[1 2] [3 4]] ; *1 obsahuje výsledek poslední vyhodnocené funkce či symbolu matrixtest.core=> (pm *1) [[1.000 2.000] [3.000 4.000]]
Konstruktory nulové matice a jednotkové matice
matrixtest.core=> (zero-matrix 2 3) [[0.0 0.0 0.0] [0.0 0.0 0.0]] matrixtest.core=> (pm *1) [[0.000 0.000 0.000] [0.000 0.000 0.000]] matrixtest.core=> (zero-matrix 4 4) [[0.0 0.0 0.0 0.0] [0.0 0.0 0.0 0.0] [0.0 0.0 0.0 0.0] [0.0 0.0 0.0 0.0]] matrixtest.core=> (pm *1) [[0.000 0.000 0.000 0.000] [0.000 0.000 0.000 0.000] [0.000 0.000 0.000 0.000] [0.000 0.000 0.000 0.000]] matrixtest.core=> (identity-matrix 4 4) [[1.0 0.0 0.0 0.0] [0.0 1.0 0.0 0.0] [0.0 0.0 1.0 0.0] [0.0 0.0 0.0 1.0]] matrixtest.core=> (pm *1) [[1.000 0.000 0.000 0.000] [0.000 1.000 0.000 0.000] [0.000 0.000 1.000 0.000] [0.000 0.000 0.000 1.000]]
Konstruktor permutační matice
; vektor udává pozice jedniček na jednotlivých řádcích matice ; rozměry matice jsou získány na základě velikosti tohoto vektoru matrixtest.core=> (permutation-matrix [1 4 2 3 0]) #NDArray [[0.0 1.0 0.0 0.0 0.0] [0.0 0.0 0.0 0.0 1.0] [0.0 0.0 1.0 0.0 0.0] [0.0 0.0 0.0 1.0 0.0] [1.0 0.0 0.0 0.0 0.0]] matrixtest.core=> (pm *1) [[0.000 1.000 0.000 0.000 0.000] [0.000 0.000 0.000 0.000 1.000] [0.000 0.000 1.000 0.000 0.000] [0.000 0.000 0.000 1.000 0.000] [1.000 0.000 0.000 0.000 0.000]]
Transpozice matice
matrixtest.core=> (def M (matrix [[1 2] [3 4]])) #'matrixtest.core/M matrixtest.core=> M [[1 2] [3 4]] matrixtest.core=> (pm *1) [[1.000 2.000] [3.000 4.000]] matrixtest.core=> (transpose M) [[1 3] [2 4]] ; vypíše se hodnota transponované matice, původní matice M se nemění matrixtest.core=> (pm *1) [[1.000 3.000] [2.000 4.000]]
Unární a binární operace nad maticemi
matrixtest.core=> (def M1 (matrix [[1 2][3 4]])) #'matrixtest.core/M1 matrixtest.core=> (def M2 (matrix [[5 6][7 8]])) #'matrixtest.core/M2 matrixtest.core=> (pm (+ M1 M2)) [[ 6.000 8.000] [10.000 12.000]] matrixtest.core=> (pm (- M1 M2)) [[-4.000 -4.000] [-4.000 -4.000]] matrixtest.core=> (pm (* M1 M2)) [[ 5.000 12.000] [21.000 32.000]] matrixtest.core=> (pm (* M1 100)) [[100.000 200.000] [300.000 400.000]] ; zde se nejdříve vypočte inverzní matice k M1 matrixtest.core=> (pm (/ M2 M1)) [[5.000 3.000] [2.333 2.000]] matrixtest.core=> (inverse M1) #NDArrayDouble [[-1.9999999999999998 1.0] [1.4999999999999998 -0.49999999999999994]] matrixtest.core=> (inverse M2) #NDArrayDouble [[-4.000000000000002 3.0000000000000013] [3.5000000000000018 -2.5000000000000013]]
Funkce vracející informaci o tom, zda je hodnota skalárem či maticí
matrixtest.core=> (def v (matrix [1 2 3 4 5 6])) #'matrixtest.core/v matrixtest.core=> (def M (matrix [[1 2] [3 4]])) #'matrixtest.core/M ; jen 42 je skalární hodnota matrixtest.core=> (for [obj [42 v M MD]] (array? obj)) (false true true true) ; jen 42 je skalární hodnota matrixtest.core=> (for [obj [42 v M MD]] (scalar? obj)) (true false false false)
Funkce vracející informace o maticích (počet dimenzí a tvar)
matrixtest.core=> (def v (matrix [1 2 3 4 5 6])) #'matrixtest.core/v matrixtest.core=> (def M (matrix [[1 2] [3 4]])) #'matrixtest.core/M ; trojrozměrná matice matrixtest.core=> (def MD (matrix [[ [1 2] [3 4] ] [ [5 6] [7 8] ] ])) #'matrixtest.core/MD matrixtest.core=> (pm MD) [[[1.000 2.000] [3.000 4.000]] [[5.000 6.000] [7.000 8.000]]] matrixtest.core=> (dimensionality v) 1 matrixtest.core=> (dimensionality M) 2 matrixtest.core=> (dimensionality MD) 3 matrixtest.core=> (dimensionality 1) 0 matrixtest.core=> (shape M) [2 2] matrixtest.core=> (shape v) [6] matrixtest.core=> (shape MD) [2 2 2]
Přečtení hodnoty prvku matice a získání řezu (slice)
matrixtest.core=> (mget M 0 0) 1 matrixtest.core=> (slice v 1) 2 ; řez 2D maticí matrixtest.core=> (slice M 1) [3 4] ; řez 3D maticí matrixtest.core=> (slice MD 1) [[5 6] [7 8]] ; operace nad řezy matrixtest.core=> (for [slice (slices M)] (apply + slice)) (3 7) ; vektorová! operace nad řezy matrixtest.core=> (apply + (slices M)) [4 6]
Změna tvaru matice
matrixtest.core=> (def v (matrix [1 2 3 4 5 6])) #'matrixtest.core/v matrixtest.core=> v [1 2 3 4 5 6] ; velmi užitečná funkce převzatá z APL: vektor převeden na matici matrixtest.core=> (reshape v [2 3]) [[1 2 3] [4 5 6]] matrixtest.core=> (pm *1) [[1.000 2.000 3.000] [4.000 5.000 6.000]] ; jiný tvar matice matrixtest.core=> (reshape v [3 2]) [[1 2] [3 4] [5 6]] matrixtest.core=> (pm *1) [[1.000 2.000] [3.000 4.000] [5.000 6.000]] matrixtest.core=> (reshape v [1 6]) [[1 2 3 4 5 6]] matrixtest.core=> (pm *1) [[1.000 2.000 3.000 4.000 5.000 6.000]] matrixtest.core=> (reshape v [6 1]) [[1] [2] [3] [4] [5] [6]] ; sloupec z vektoru matrixtest.core=> (pm *1) [[1.000] [2.000] [3.000] [4.000] [5.000] [6.000]]
Využití makra → ke kompozici operací
; jedná se o oneliner rozepsaný kvůli větší čitelnosti na čtyři řádky (-> (matrix (range 1 101)) (reshape [10 10]) transpose pm) [[ 1.000 11.000 21.000 31.000 41.000 51.000 61.000 71.000 81.000 91.000] [ 2.000 12.000 22.000 32.000 42.000 52.000 62.000 72.000 82.000 92.000] [ 3.000 13.000 23.000 33.000 43.000 53.000 63.000 73.000 83.000 93.000] [ 4.000 14.000 24.000 34.000 44.000 54.000 64.000 74.000 84.000 94.000] [ 5.000 15.000 25.000 35.000 45.000 55.000 65.000 75.000 85.000 95.000] [ 6.000 16.000 26.000 36.000 46.000 56.000 66.000 76.000 86.000 96.000] [ 7.000 17.000 27.000 37.000 47.000 57.000 67.000 77.000 87.000 97.000] [ 8.000 18.000 28.000 38.000 48.000 58.000 68.000 78.000 88.000 98.000] [ 9.000 19.000 29.000 39.000 49.000 59.000 69.000 79.000 89.000 99.000] [10.000 20.000 30.000 40.000 50.000 60.000 70.000 80.000 90.000 100.000]] ; sekvence operací aplikovaných na matici M1 (-> M1 transpose inverse (* 10000) transpose (* M2) (+ M1) pm) [[-99999.000 60002.000] [105003.000 -39996.000]]
17. Obsah následující části seriálu
V navazující části seriálu o světě lispovských jazyků si představíme pravděpodobně nejrozsáhlejší a nejúplnější implementaci programovacího jazyka Scheme. Jedná se o jazyk Racket, který je dodáván i s interaktivním vývojovým prostředím a množstvím přídavných modulů pokrývajících různá odvětví informatiky (včetně počítačové grafiky, numerických výpočtů atd.).
Obrázek 1: Logo projektu Racket.
18. Repositář s demonstračními příklady
Zdrojové kódy všech dnes použitých demonstračních příkladů naprogramovaných v jazyce Kawa byly uloženy do Git repositáře, který je dostupný na adrese https://github.com/tisnik/lisp-families.git (stále na GitHubu :-). V případě, že nebudete chtít klonovat celý repositář (ten je ovšem – alespoň prozatím – velmi malý, můžete namísto toho použít odkazy na jednotlivé příklady, které naleznete v následující tabulce:
19. Literatura
- Peter Seibel
„Practical Common Lisp“
2009 - Paul Graham
„ANSI Common Lisp“
1995 - Gerald Gazdar
„Natural Language Processing in Lisp: An Introduction to Computational Linguistics“
1989 - Peter Norvig
„Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp“
1991 - Alex Mileler et.al.
„Clojure Applied: From Practice to Practitioner“
2015 - „Living Clojure: An Introduction and Training Plan for Developers“
2015 - Dmitri Sotnikov
„Web Development with Clojure: Build Bulletproof Web Apps with Less Code“
2016 - McCarthy
„Recursive functions of symbolic expressions and their computation by machine, part I“
1960 - R. Kent Dybvig
„The Scheme Programming Language“
2009 - Max Hailperin
„Concrete Abstractions“
1998 - 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 - Harold Abelson, Gerald Jay Sussman, Julie Sussman:
Structure and Interpretation of Computer Programs
MIT Press. 1985, 1996 (a možná vyšel i další přetisk) - Paul Graham
On Lisp
Prentice Hall, 1993
Dostupné online na stránce http://www.paulgraham.com/onlisptext.html - David S. Touretzky
Common LISP: A Gentle Introduction to Symbolic Computation (Dover Books on Engineering)
- Peter Norvig
Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp - Patrick Winston, Berthold Horn
Lisp (3rd Edition)
ISBN-13: 978–0201083194, ISBN-10: 0201083191 - Matthias Felleisen, David Van Horn, Dr. Conrad Barski
Realm of Racket: Learn to Program, One Game at a Time!
ISBN-13: 978–1593274917, ISBN-10: 1593274912
20. Odkazy na Internetu
- Vector Library (R7RS-compatible)
https://srfi.schemers.org/srfi-133/srfi-133.html - Vectors (pro Gauche)
https://practical-scheme.net/gauche/man/gauche-refe/Vectors.html - Kawa: Compiling Scheme to Java
https://www.mit.edu/afs.new/sipb/project/kawa/doc/kawa-tour.html - Kawa in Languages shootout
http://per.bothner.com/blog/2010/Kawa-in-shootout/ - Kawa 2.0 Supports Scheme R7RS
https://developers.slashdot.org/story/14/12/13/2259225/kawa-20-supports-scheme-r7rs/ - Kawa — fast scripting on the Java platform
https://lwn.net/Articles/623349/ - Tail call (a její optimalizace)
https://en.wikipedia.org/wiki/Tail_call - SLIME (Wikipedia)
http://en.wikipedia.org/wiki/SLIME - slime.vim
http://s3.amazonaws.com/mps/slime.vim - What are the best scheme implementations?
https://www.slant.co/topics/5282/~scheme-implementations - Bigloo homepage
http://www-sop.inria.fr/mimosa/fp/Bigloo/ - FTP s tarbally Bigloo
ftp://ftp-sop.inria.fr/indes/fp/Bigloo - GOTO 2018 • Functional Programming in 40 Minutes • Russ Olsen
https://www.youtube.com/watch?v=0if71HOyVjY - TinyScheme (stránka na Sourceforge)
http://tinyscheme.sourceforge.net/home.html - Embedding Tiny Scheme in a Game
http://www.silicondelight.com/embedding-tiny-scheme-in-a-game/ - Embedding Scheme for a game mission scripting DSL
http://carloscarrasco.com/embedding-scheme-for-a-game-mission-scripting-dsl.html - Všechny verze TinyScheme na SourceForge
https://sourceforge.net/projects/tinyscheme/files/tinyscheme/ - Fork TinyScheme na GitHubu
https://github.com/yawnt/tinyscheme - Ackermannova funkce
https://cs.wikipedia.org/wiki/Ackermannova_funkce - Ackermann function na Rosetta Code
https://rosettacode.org/wiki/Ackermann_function#Scheme - Success Stories (lisp.org)
https://lisp-lang.org/success/ - Allegro Common Lisp Success Stories
https://franz.com/success/ - Clojure Success Stories
https://clojure.org/community/success_stories - Scheme Quick Reference
https://www.st.cs.uni-saarland.de/edu/config-ss04/scheme-quickref.pdf - Slajdy o Scheme (od slajdu číslo 15)
https://docs.google.com/presentation/d/1abmDnKjrq1tcjGvvRNAKhOiSTSE2lyagtcEPal07Gbo/edit - Scheme Cheat Sheet
https://github.com/smythp/scheme-cheat-sheet - Embedding Lua, embedding Guile
http://puntoblogspot.blogspot.com/2013/04/embedding-lua-embedding-guile.html - Lambda Papers
https://en.wikisource.org/wiki/Lambda_Papers - Revised7Report on the Algorithmic Language Scheme
https://small.r7rs.org/attachment/r7rs.pdf - Video Lectures (MIT, SICP 2005)
https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6–001-structure-and-interpretation-of-computer-programs-spring-2005/video-lectures/ - Why is Scheme my first language in university?
https://softwareengineering.stackexchange.com/questions/115252/why-is-scheme-my-first-language-in-university - The Perils of JavaSchools
https://www.joelonsoftware.com/2005/12/29/the-perils-of-javaschools-2/ - How to Design Programs, Second Edition
https://htdp.org/2019–02–24/index.html - LilyPond
http://lilypond.org/ - LilyPond — Extending (přes Scheme)
http://lilypond.org/doc/v2.18/Documentation/extending/scheme-tutorial - Scheme in LilyPond
http://lilypond.org/doc/v2.18/Documentation/extending/scheme-in-lilypond - GnuCash
http://www.gnucash.org/ - Custom Reports (in GNU Cash)
https://wiki.gnucash.org/wiki/Custom_Reports - Program by Design
https://programbydesign.org/ - SchemePy
https://pypi.org/project/SchemePy/ - LISP FQA: Section – [1–5] What is the „minimal“ set of primitives needed for a Lisp interpreter?
http://www.faqs.org/faqs/lisp-faq/part1/section-6.html - femtolisp
https://github.com/JeffBezanson/femtolisp - (How to Write a (Lisp) Interpreter (in Python))
http://norvig.com/lispy.html - Repositář s Guile Emacsem
http://git.hcoop.net/?p=bpt/guile.git - Interacting with Guile Compound Data Types in C
http://www.lonelycactus.com/guilebook/x1555.html - Calling Guile functions from C
http://www.lonelycactus.com/guilebook/c1204.html#SECCALLGUILEFUNC - Arrays, and other compound data types
http://www.lonelycactus.com/guilebook/charrays.html - Interacting with Guile Compound Data Types in C
http://www.lonelycactus.com/guilebook/x1555.html - Guile Reference Manual
https://www.gnu.org/software/guile/manual/html_node/index.html - Scheme: Summary of Common Syntax
https://www.gnu.org/software/guile/manual/html_node/Syntax-Summary.html#Syntax-Summary - Scripting with Guile: Extension language enhances C and Scheme
https://www.ibm.com/developerworks/library/l-guile/index.html - Having fun with Guile: a tutorial
http://dustycloud.org/misc/guile-tutorial.html - Guile: Loading Readline Support
https://www.gnu.org/software/guile/manual/html_node/Loading-Readline-Support.html#Loading-Readline-Support - lispy
https://pypi.org/project/lispy/ - Lython
https://pypi.org/project/Lython/ - Lizpop
https://pypi.org/project/lizpop/ - Budoucnost programovacích jazyků
http://www.knesl.com/budoucnost-programovacich-jazyku - LISP Prolog and Evolution
http://blog.samibadawi.com/2013/05/lisp-prolog-and-evolution.html - List of Lisp-family programming languages
https://en.wikipedia.org/wiki/List_of_Lisp-family_programming_languages - clojure_py na indexu PyPi
https://pypi.python.org/pypi/clojure_py - PyClojure
https://github.com/eigenhombre/PyClojure - Hy na GitHubu
https://github.com/hylang/hy - Hy: The survival guide
https://notes.pault.ag/hy-survival-guide/ - Hy běžící na monitoru terminálu společnosti Symbolics
http://try-hy.appspot.com/ - Welcome to Hy’s documentation!
http://docs.hylang.org/en/stable/ - Hy na PyPi
https://pypi.org/project/hy/#description - Getting Hy on Python
https://lwn.net/Articles/596626/ - Programming Can Be Fun with Hy
https://opensourceforu.com/2014/02/programming-can-fun-hy/ - Přednáška o projektu Hy (pětiminutový lighttalk)
http://blog.pault.ag/day/2013/04/02 - Hy (Wikipedia)
https://en.wikipedia.org/wiki/Hy - GNU Emacs Lisp Reference Manual: Point
https://www.gnu.org/software/emacs/manual/html_node/elisp/Point.html - GNU Emacs Lisp Reference Manual: Narrowing
https://www.gnu.org/software/emacs/manual/html_node/elisp/Narrowing.html - GNU Emacs Lisp Reference Manual: Functions that Create Markers
https://www.gnu.org/software/emacs/manual/html_node/elisp/Creating-Markers.html - GNU Emacs Lisp Reference Manual: Motion
https://www.gnu.org/software/emacs/manual/html_node/elisp/Motion.html#Motion - GNU Emacs Lisp Reference Manual: Basic Char Syntax
https://www.gnu.org/software/emacs/manual/html_node/elisp/Basic-Char-Syntax.html - Elisp: Sequence: List, Array
http://ergoemacs.org/emacs/elisp_list_vs_vector.html - Elisp: Property List
http://ergoemacs.org/emacs/elisp_property_list.html - Elisp: Hash Table
http://ergoemacs.org/emacs/elisp_hash_table.html - Elisp: Association List
http://ergoemacs.org/emacs/elisp_association_list.html - The mapcar Function (An Introduction to Programming in Emacs Lisp)
https://www.gnu.org/software/emacs/manual/html_node/eintr/mapcar.html - Anaphoric macro
https://en.wikipedia.org/wiki/Anaphoric_macro - Some Common Lisp Loop Macro Examples
https://www.youtube.com/watch?v=3yl8o6r_omw - A Guided Tour of Emacs
https://www.gnu.org/software/emacs/tour/ - The Roots of Lisp
http://www.paulgraham.com/rootsoflisp.html - Evil (Emacs Wiki)
https://www.emacswiki.org/emacs/Evil - Evil (na GitHubu)
https://github.com/emacs-evil/evil - Evil (na stránkách repositáře MELPA)
https://melpa.org/#/evil - Evil Mode: How I Switched From VIM to Emacs
https://blog.jakuba.net/2014/06/23/evil-mode-how-to-switch-from-vim-to-emacs.html - GNU Emacs (home page)
https://www.gnu.org/software/emacs/ - GNU Emacs (texteditors.org)
http://texteditors.org/cgi-bin/wiki.pl?GnuEmacs - An Introduction To Using GDB Under Emacs
http://tedlab.mit.edu/~dr/gdbintro.html - An Introduction to Programming in Emacs Lisp
https://www.gnu.org/software/emacs/manual/html_node/eintr/index.html - 27.6 Running Debuggers Under Emacs
https://www.gnu.org/software/emacs/manual/html_node/emacs/Debuggers.html - GdbMode
http://www.emacswiki.org/emacs/GdbMode - Emacs (Wikipedia)
https://en.wikipedia.org/wiki/Emacs - Emacs timeline
http://www.jwz.org/doc/emacs-timeline.html - Emacs Text Editors Family
http://texteditors.org/cgi-bin/wiki.pl?EmacsFamily - Vrapper aneb spojení možností Vimu a Eclipse
https://mojefedora.cz/vrapper-aneb-spojeni-moznosti-vimu-a-eclipse/ - Vrapper aneb spojení možností Vimu a Eclipse (část 2: vyhledávání a nahrazování textu)
https://mojefedora.cz/vrapper-aneb-spojeni-moznosti-vimu-a-eclipse-cast-2-vyhledavani-a-nahrazovani-textu/ - Emacs/Evil-mode – A basic reference to using evil mode in Emacs
http://www.aakarshnair.com/posts/emacs-evil-mode-cheatsheet - From Vim to Emacs+Evil chaotic migration guide
https://juanjoalvarez.net/es/detail/2014/sep/19/vim-emacsevil-chaotic-migration-guide/ - Introduction to evil-mode {video)
https://www.youtube.com/watch?v=PeVQwYUxYEg - 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 - evil-numbers
https://github.com/cofi/evil-numbers - Debuggery a jejich nadstavby v Linuxu (1.část)
http://fedora.cz/debuggery-a-jejich-nadstavby-v-linuxu/ - Debuggery a jejich nadstavby v Linuxu (2.část)
http://fedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-2-cast/ - Debuggery a jejich nadstavby v Linuxu (3): Nemiver
http://fedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-3-nemiver/ - Debuggery a jejich nadstavby v Linuxu (4): KDbg
http://fedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-4-kdbg/ - Debuggery a jejich nadstavby v Linuxu (5): ladění aplikací v editorech Emacs a Vim
https://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-5-ladeni-aplikaci-v-editorech-emacs-a-vim/ - Org mode
https://orgmode.org/ - The Org Manual
https://orgmode.org/manual/index.html - Kakoune (modální textový editor)
http://kakoune.org/ - Vim-style keybinding in Emacs/Evil-mode
https://gist.github.com/troyp/6b4c9e1c8670200c04c16036805773d8 - Emacs – jak začít
http://www.abclinuxu.cz/clanky/navody/emacs-jak-zacit - Programovací jazyk LISP a LISP machines
https://www.root.cz/clanky/programovaci-jazyk-lisp-a-lisp-machines/ - Evil-surround
https://github.com/emacs-evil/evil-surround - Spacemacs
http://spacemacs.org/ - Lisp: Common Lisp, Racket, Clojure, Emacs Lisp
http://hyperpolyglot.org/lisp - Common Lisp, Scheme, Clojure, And Elisp Compared
http://irreal.org/blog/?p=725 - Does Elisp Suck?
http://irreal.org/blog/?p=675 - Emacs pro mírně pokročilé (9): Elisp
https://www.root.cz/clanky/emacs-elisp/ - If I want to learn lisp, are emacs and elisp a good choice?
https://www.reddit.com/r/emacs/comments/2m141y/if_i_want_to_learn_lisp_are_emacs_and_elisp_a/ - Clojure(Script) Interactive Development Environment that Rocks!
https://github.com/clojure-emacs/cider - An Introduction to Emacs Lisp
https://harryrschwartz.com/2014/04/08/an-introduction-to-emacs-lisp.html - Emergency Elisp
http://steve-yegge.blogspot.com/2008/01/emergency-elisp.html - Lambda calculus
https://en.wikipedia.org/wiki/Lambda_calculus - John McCarthy's original LISP paper from 1959
https://www.reddit.com/r/programming/comments/17lpz4/john_mccarthys_original_lisp_paper_from_1959/ - Micro Manual LISP
https://www.scribd.com/document/54050141/Micro-Manual-LISP - How Lisp Became God's Own Programming Language
https://twobithistory.org/2018/10/14/lisp.html - History of Lisp
http://jmc.stanford.edu/articles/lisp/lisp.pdf - The Roots of Lisp
http://languagelog.ldc.upenn.edu/myl/llog/jmc.pdf - Racket
https://racket-lang.org/ - The Racket Manifesto
http://felleisen.org/matthias/manifesto/ - MIT replaces Scheme with Python
https://www.johndcook.com/blog/2009/03/26/mit-replaces-scheme-with-python/ - Adventures in Advanced Symbolic Programming
http://groups.csail.mit.edu/mac/users/gjs/6.945/ - Why MIT Switched from Scheme to Python (2009)
https://news.ycombinator.com/item?id=14167453 - Starodávná stránka XLispu
http://www.xlisp.org/ - AutoLISP
https://en.wikipedia.org/wiki/AutoLISP - Seriál PicoLisp: minimalistický a výkonný interpret Lispu
https://www.root.cz/serialy/picolisp-minimalisticky-a-vykonny-interpret-lispu/ - Common Lisp
https://common-lisp.net/ - Getting Going with Common Lisp
https://cliki.net/Getting%20Started - Online Tutorial (Common Lisp)
https://cliki.net/online%20tutorial - Guile Emacs
https://www.emacswiki.org/emacs/GuileEmacs - Guile Emacs History
https://www.emacswiki.org/emacs/GuileEmacsHistory - Guile is a programming language
https://www.gnu.org/software/guile/ - MIT Scheme
http://groups.csail.mit.edu/mac/projects/scheme/ - SIOD: Scheme in One Defun
http://people.delphiforums.com/gjc//siod.html - CommonLispForEmacs
https://www.emacswiki.org/emacs/CommonLispForEmacs - Elisp: print, princ, prin1, format, message
http://ergoemacs.org/emacs/elisp_printing.html - Special Forms in Lisp
http://www.nhplace.com/kent/Papers/Special-Forms.html - Basic Building Blocks in LISP
https://www.tutorialspoint.com/lisp/lisp_basic_syntax.htm - Introduction to LISP – University of Pittsburgh
https://people.cs.pitt.edu/~milos/courses/cs2740/Lectures/LispTutorial.pdf - Why don't people use LISP
https://forums.freebsd.org/threads/why-dont-people-use-lisp.24572/ - Structured program theorem
https://en.wikipedia.org/wiki/Structured_program_theorem - Clojure: API Documentation
https://clojure.org/api/api - Tutorial for the Common Lisp Loop Macro
http://www.ai.sri.com/pkarp/loop.html - Common Lisp's Loop Macro Examples for Beginners
http://www.unixuser.org/~euske/doc/cl/loop.html - A modern list api for Emacs. No 'cl required.
https://github.com/magnars/dash.el - The LOOP Facility
http://www.lispworks.com/documentation/HyperSpec/Body/06_a.htm - Clojure.org: Vars and the Global Environment
http://clojure.org/Vars - Clojure.org: Refs and Transactions
http://clojure.org/Refs - Clojure.org: Atoms
http://clojure.org/Atoms - Clojure.org: Agents as Asynchronous Actions
http://clojure.org/agents - Transient Data Structureshttp://clojure.org/transients
- Dynamic Languages Strike Back
http://steve-yegge.blogspot.cz/2008/05/dynamic-languages-strike-back.html - Scripting: Higher Level Programming for the 21st Century
http://www.tcl.tk/doc/scripting.html - Clojure (na Wikipedia EN)
http://en.wikipedia.org/wiki/Clojure - Clojure (na Wikipedia CS)
http://cs.wikipedia.org/wiki/Clojure - SICP (The Structure and Interpretation of Computer Programs)
http://mitpress.mit.edu/sicp/ - Pure function
http://en.wikipedia.org/wiki/Pure_function - Funkcionální programování
http://cs.wikipedia.org/wiki/Funkcionální_programování - Jazyky Hy a Clojure-py: moderní dialekty LISPu určené pro Python VM
https://www.root.cz/clanky/jazyky-hy-a-clojure-py-moderni-dialekty-lispu-urcene-pro-python-vm/ - Pixie: lehký skriptovací jazyk s „kouzelnými“ schopnostmi
https://www.root.cz/clanky/pixie-lehky-skriptovaci-jazyk-s-kouzelnymi-schopnostmi/ - Programovací jazyk Pixie: funkce ze základní knihovny a použití FFI
https://www.root.cz/clanky/programovaci-jazyk-pixie-funkce-ze-zakladni-knihovny-a-pouziti-ffi/ - Stránka projektu Jython
http://www.jython.org/ - Jython (Wikipedia)
https://en.wikipedia.org/wiki/Jython - Scripting for the Java Platform (Wikipedia)
https://en.wikipedia.org/wiki/Scripting_for_the_Java_Platform - JSR 223: Scripting for the JavaTM Platform
https://jcp.org/en/jsr/detail?id=223 - List of JVM languages
https://en.wikipedia.org/wiki/List_of_JVM_languages - The JavaTM Virtual Machine Specification, Second Edition
http://java.sun.com/docs/books/jvms/second_edition/html/VMSpecTOC.doc.html - The class File Format
http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html - javap – The Java Class File Disassembler
http://docs.oracle.com/javase/1.4.2/docs/tooldocs/windows/javap.html - javap-java-1.6.0-openjdk(1) – Linux man page
http://linux.die.net/man/1/javap-java-1.6.0-openjdk - Using javap
http://www.idevelopment.info/data/Programming/java/miscellaneous_java/Using_javap.html - Examine class files with the javap command
http://www.techrepublic.com/article/examine-class-files-with-the-javap-command/5815354