Hlavní navigace

Zpracování vektorů, matic a N-rozměrných polí v programovacím jazyku Kawa

Pavel Tišnovský

V dnešní části seriálu o světě lispovských programovacích jazyků si ukážeme, jakým způsobem se v jazyku Kawa pracuje s vektory a maticemi. Tato velmi důležitá oblast informatiky, která je někdy nazývána array programming, se neustále rozvíjí, což zasáhlo i jazyky postavené na LISPu (Racket, Kawa, Clojure).

Doba čtení: 40 minut

Sdílet

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

16. Knihovna core.matrix

17. Obsah následující části seriálu

18. Repositář s demonstračními příklady

19. Literatura

20. Odkazy na Internetu

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:

  1. SIMD instrukce využívané v moderních mikroprocesorech řady x86
    https://www.root.cz/clanky/simd-instrukce-vyuzivane-v-modernich-mikroprocesorech-rady-x86/
  2. 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/
  3. 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:

  1. Programování mainframů: jazyk APL
    https://www.root.cz/clanky/pro­gramovani-mainframu-jazyk-apl/
  2. Programovací jazyk APL: programování bez smyček
    https://www.root.cz/clanky/pro­gramovaci-jazyk-apl-programovani-bez-smycek/
  3. Programovací jazyk APL – dokončení
    https://www.root.cz/clanky/pro­gramovaci-jazyk-apl-dokonceni/
  4. Programovací jazyk J – od hieroglyfů k ASCII znakům
    https://www.root.cz/clanky/pro­gramovaci-jazyk-j-ndash-od-hieroglyfu-k-nbsp-ascii-znakum/
  5. Programujeme v jazyku J: vektory a matice
    https://www.root.cz/clanky/pro­gramujeme-v-jazyku-j-ndash-vektory-a-matice/
  6. Programovací jazyk J: operátory, uživatelské funkce a tacit programming
    https://www.root.cz/clanky/pro­gramovaci-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:

  1. 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/
  2. 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/
  3. 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.

Poznámka: část věnovaná jazyku Clojure byla zařazena z toho důvodu, že jak Clojure, tak i Kawa sdílí stejný ekosystém JVM.

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]
Poznámka: povšimněte si, že jméno funkce měnící pole končí vykřičníkem, podobně jako další podobně koncipované funkce, které obecně mění stav aplikace. Dále si povšimněte, že se prvky indexují od nuly, stejně jako v C a Javě.

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
Poznámka: vidíme, že nyní se každý prvek inicializuje trojicí instrukcí iconst/bipush (načtení indexu) + ldc (načtení konstanty) + fastore (uložení do pole).

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)
Poznámka: chyba nastala při přístupu k prvku s indexem 10, který ve čtyřprvkovém vektoru pochopitelně neexistuje.

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║
╚══════╧══════╝
Poznámka: povšimněte si, jakým způsobem interpret programovacího jazyka Kawa zobrazuje obsah zkonstruovaného pole. U jednorozměrných a dvourozměrných polí zobrazuje tabulku s obsahem jednotlivých prvků, přičemž je na prvním řádku upřesněn jak počet dimenzí, tak i rozsah indexů v jednotlivých dimenzích (zde konkrétně počet řádků oddělený od počtu sloupců dvojtečkou).

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║
╚══════╧══════╧══════╧══════╝
Poznámka: u trojrozměrné a taktéž u vícerozměrných struktur je již nutné použít oddělovač jednotlivých 2D podmatic tak, jak je to ukázáno na předchozím výstupu z interpretru programovacího jazyka Kawa. Podrobnější informace najdete na stránce https://www.gnu.org/softwa­re/guile/manual/html_node/A­rray-Syntax.html.

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/mi­keranderson/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
Poznámka: původní tabulka byla upravena a doplněna.

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:

# Příklad Popis příkladu Cesta
1 Array1.scm vytvoření jednorozměrného pole s prvky typu int https://github.com/tisnik/lisp-families/blob/master/kawa/Array1.scm
2 Array1.asm disassemblovaný soubor .class předchozího skriptu https://github.com/tisnik/lisp-families/blob/master/kawa/Array1.asm
3 Array2_exception.scm výjimka při přístupu k neexistujícímu prvku https://github.com/tisnik/lisp-families/blob/master/kawa/A­rray2_exception.scm
4 Array2.scm vytvoření pole a přístup k jeho prvkům https://github.com/tisnik/lisp-families/blob/master/kawa/Array2.scm
5 Array2.asm disassemblovaný soubor .class předchozího skriptu https://github.com/tisnik/lisp-families/blob/master/kawa/Array2.asm
6 Array3.scm pole s odlišnými hodnotami https://github.com/tisnik/lisp-families/blob/master/kawa/Array3.scm
7 Array3.asm disassemblovaný soubor .class předchozího skriptu https://github.com/tisnik/lisp-families/blob/master/kawa/Array3.asm
8 Array4.scm pole s prvky typu float https://github.com/tisnik/lisp-families/blob/master/kawa/Array4.scm
9 Array4.asm disassemblovaný soubor .class předchozího skriptu https://github.com/tisnik/lisp-families/blob/master/kawa/Array4.asm
10 CreateArray1.scm funkce pro vytvoření pole zadané délky https://github.com/tisnik/lisp-families/blob/master/kawa/Cre­ateArray1.scm
11 CreateArray1.asm disassemblovaný soubor .class předchozího skriptu https://github.com/tisnik/lisp-families/blob/master/kawa/Cre­ateArray1.asm
12 CreateArray2.scm vylepšení funkce pro vytvoření pole zadané délky https://github.com/tisnik/lisp-families/blob/master/kawa/Cre­ateArray2.scm
13 CreateArray2.asm disassemblovaný soubor .class předchozího skriptu https://github.com/tisnik/lisp-families/blob/master/kawa/Cre­ateArray2.asm
14 CreateArray3.scm vytvoření pole řetězců https://github.com/tisnik/lisp-families/blob/master/kawa/Cre­ateArray3.scm
15 CreateArray3.asm disassemblovaný soubor .class předchozího skriptu https://github.com/tisnik/lisp-families/blob/master/kawa/Cre­ateArray3.asm
16 Matrix1.scm matice 3×3 prvky https://github.com/tisnik/lisp-families/blob/master/kawa/Matrix1.scm
17 Matrix2.scm nepravidelná matice s prvky typu int https://github.com/tisnik/lisp-families/blob/master/kawa/Matrix2.scm
18 Matrix3.scm nepravidelná matice s prvky typu float https://github.com/tisnik/lisp-families/blob/master/kawa/Matrix3.scm
19 ranges1.scm příklad použití rozsahů https://github.com/tisnik/lisp-families/blob/master/kawa/ranges1.scm
20 ranges2.scm výběr prvků s využitím rozsahů https://github.com/tisnik/lisp-families/blob/master/kawa/ranges2.scm
21 Vectors1.scm jednorozměrné vektory https://github.com/tisnik/lisp-families/blob/master/kawa/Vectors1.scm
22 Vectors2.scm přístup k prvkům vektorů a modifikace jejich hodnot https://github.com/tisnik/lisp-families/blob/master/kawa/Vectors2.scm
23 Makefile soubor Makefile určený pro překlad demonstračních skriptů do bajtkódu a pro jejich disassembling https://github.com/tisnik/lisp-families/blob/master/kawa/Makefile

19. Literatura

  1. Peter Seibel
    „Practical Common Lisp“
    2009
  2. Paul Graham
    „ANSI Common Lisp“
    1995
  3. Gerald Gazdar
    „Natural Language Processing in Lisp: An Introduction to Computational Linguistics“
    1989
  4. Peter Norvig
    „Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp“
    1991
  5. Alex Mileler et.al.
    „Clojure Applied: From Practice to Practitioner“
    2015
  6. „Living Clojure: An Introduction and Training Plan for Developers“
    2015
  7. Dmitri Sotnikov
    „Web Development with Clojure: Build Bulletproof Web Apps with Less Code“
    2016
  8. McCarthy
    „Recursive functions of symbolic expressions and their computation by machine, part I“
    1960
  9. R. Kent Dybvig
    „The Scheme Programming Language“
    2009
  10. Max Hailperin
    „Concrete Abstractions“
    1998
  11. Guy L. Steele
    „History of Scheme“
    2006, Sun Microsystems Laboratories
  12. Kolář J., Muller K.:
    „Speciální programovací jazyky“
    Praha 1981
  13. „AutoLISP Release 9, Programmer's reference“
    Autodesk Ltd., October 1987
  14. „AutoLISP Release 10, Programmer's reference“
    Autodesk Ltd., September 1988
  15. 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
  16. Carl Hewitt; Peter Bishop and Richard Steiger
    „A Universal Modular Actor Formalism for Artificial Intelligence“
    1973
  17. Feiman, J.
    „The Gartner Programming Language Survey (October 2001)“
    Gartner Advisory
  18. 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)
  19. Paul Graham
    On Lisp
    Prentice Hall, 1993
    Dostupné online na stránce http://www.paulgraham.com/on­lisptext.html
  20. David S. Touretzky
    Common LISP: A Gentle Introduction to Symbolic Computation (Dover Books on Engineering)
  21. Peter Norvig
    Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp
  22. Patrick Winston, Berthold Horn
    Lisp (3rd Edition)
    ISBN-13: 978–0201083194, ISBN-10: 0201083191
  23. 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

  1. Vector Library (R7RS-compatible)
    https://srfi.schemers.org/srfi-133/srfi-133.html
  2. Vectors (pro Gauche)
    https://practical-scheme.net/gauche/man/gauche-refe/Vectors.html
  3. Kawa: Compiling Scheme to Java
    https://www.mit.edu/afs.new/sip­b/project/kawa/doc/kawa-tour.html
  4. Kawa in Languages shootout
    http://per.bothner.com/blog/2010/Kawa-in-shootout/
  5. Kawa 2.0 Supports Scheme R7RS
    https://developers.slashdot­.org/story/14/12/13/2259225/ka­wa-20-supports-scheme-r7rs/
  6. Kawa — fast scripting on the Java platform
    https://lwn.net/Articles/623349/
  7. Tail call (a její optimalizace)
    https://en.wikipedia.org/wi­ki/Tail_call
  8. SLIME (Wikipedia)
    http://en.wikipedia.org/wiki/SLIME
  9. slime.vim
    http://s3.amazonaws.com/mps/slime.vim
  10. What are the best scheme implementations?
    https://www.slant.co/topic­s/5282/~scheme-implementations
  11. Bigloo homepage
    http://www-sop.inria.fr/mimosa/fp/Bigloo/
  12. FTP s tarbally Bigloo
    ftp://ftp-sop.inria.fr/indes/fp/Bigloo
  13. GOTO 2018 • Functional Programming in 40 Minutes • Russ Olsen
    https://www.youtube.com/wat­ch?v=0if71HOyVjY
  14. TinyScheme (stránka na Sourceforge)
    http://tinyscheme.sourcefor­ge.net/home.html
  15. Embedding Tiny Scheme in a Game
    http://www.silicondelight­.com/embedding-tiny-scheme-in-a-game/
  16. Embedding Scheme for a game mission scripting DSL
    http://carloscarrasco.com/embedding-scheme-for-a-game-mission-scripting-dsl.html
  17. Všechny verze TinyScheme na SourceForge
    https://sourceforge.net/pro­jects/tinyscheme/files/ti­nyscheme/
  18. Fork TinyScheme na GitHubu
    https://github.com/yawnt/tinyscheme
  19. Ackermannova funkce
    https://cs.wikipedia.org/wi­ki/Ackermannova_funkce
  20. Ackermann function na Rosetta Code
    https://rosettacode.org/wi­ki/Ackermann_function#Sche­me
  21. Success Stories (lisp.org)
    https://lisp-lang.org/success/
  22. Allegro Common Lisp Success Stories
    https://franz.com/success/
  23. Clojure Success Stories
    https://clojure.org/commu­nity/success_stories
  24. Scheme Quick Reference
    https://www.st.cs.uni-saarland.de/edu/config-ss04/scheme-quickref.pdf
  25. Slajdy o Scheme (od slajdu číslo 15)
    https://docs.google.com/pre­sentation/d/1abmDnKjrq1tcjGvvRNAK­hOiSTSE2lyagtcEPal07Gbo/e­dit
  26. Scheme Cheat Sheet
    https://github.com/smythp/scheme-cheat-sheet
  27. Embedding Lua, embedding Guile
    http://puntoblogspot.blog­spot.com/2013/04/embedding-lua-embedding-guile.html
  28. Lambda Papers
    https://en.wikisource.org/wi­ki/Lambda_Papers
  29. Revised7Report on the Algorithmic Language Scheme
    https://small.r7rs.org/at­tachment/r7rs.pdf
  30. 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/
  31. Why is Scheme my first language in university?
    https://softwareengineerin­g.stackexchange.com/questi­ons/115252/why-is-scheme-my-first-language-in-university
  32. The Perils of JavaSchools
    https://www.joelonsoftware­.com/2005/12/29/the-perils-of-javaschools-2/
  33. How to Design Programs, Second Edition
    https://htdp.org/2019–02–24/index.html
  34. LilyPond
    http://lilypond.org/
  35. LilyPond — Extending (přes Scheme)
    http://lilypond.org/doc/v2­.18/Documentation/extendin­g/scheme-tutorial
  36. Scheme in LilyPond
    http://lilypond.org/doc/v2­.18/Documentation/extendin­g/scheme-in-lilypond
  37. GnuCash
    http://www.gnucash.org/
  38. Custom Reports (in GNU Cash)
    https://wiki.gnucash.org/wi­ki/Custom_Reports
  39. Program by Design
    https://programbydesign.org/
  40. SchemePy
    https://pypi.org/project/SchemePy/
  41. 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
  42. femtolisp
    https://github.com/JeffBe­zanson/femtolisp
  43. (How to Write a (Lisp) Interpreter (in Python))
    http://norvig.com/lispy.html
  44. Repositář s Guile Emacsem
    http://git.hcoop.net/?p=bpt/guile.git
  45. Interacting with Guile Compound Data Types in C
    http://www.lonelycactus.com/gu­ilebook/x1555.html
  46. Calling Guile functions from C
    http://www.lonelycactus.com/gu­ilebook/c1204.html#SECCAL­LGUILEFUNC
  47. Arrays, and other compound data types
    http://www.lonelycactus.com/gu­ilebook/charrays.html
  48. Interacting with Guile Compound Data Types in C
    http://www.lonelycactus.com/gu­ilebook/x1555.html
  49. Guile Reference Manual
    https://www.gnu.org/softwa­re/guile/manual/html_node/in­dex.html
  50. Scheme: Summary of Common Syntax
    https://www.gnu.org/softwa­re/guile/manual/html_node/Syn­tax-Summary.html#Syntax-Summary
  51. Scripting with Guile: Extension language enhances C and Scheme
    https://www.ibm.com/develo­perworks/library/l-guile/index.html
  52. Having fun with Guile: a tutorial
    http://dustycloud.org/misc/guile-tutorial.html
  53. Guile: Loading Readline Support
    https://www.gnu.org/softwa­re/guile/manual/html_node/Lo­ading-Readline-Support.html#Loading-Readline-Support
  54. lispy
    https://pypi.org/project/lispy/
  55. Lython
    https://pypi.org/project/Lython/
  56. Lizpop
    https://pypi.org/project/lizpop/
  57. Budoucnost programovacích jazyků
    http://www.knesl.com/budoucnost-programovacich-jazyku
  58. LISP Prolog and Evolution
    http://blog.samibadawi.com/2013/05/lisp-prolog-and-evolution.html
  59. List of Lisp-family programming languages
    https://en.wikipedia.org/wi­ki/List_of_Lisp-family_programming_languages
  60. clojure_py na indexu PyPi
    https://pypi.python.org/py­pi/clojure_py
  61. PyClojure
    https://github.com/eigenhom­bre/PyClojure
  62. Hy na GitHubu
    https://github.com/hylang/hy
  63. Hy: The survival guide
    https://notes.pault.ag/hy-survival-guide/
  64. Hy běžící na monitoru terminálu společnosti Symbolics
    http://try-hy.appspot.com/
  65. Welcome to Hy’s documentation!
    http://docs.hylang.org/en/stable/
  66. Hy na PyPi
    https://pypi.org/project/hy/#des­cription
  67. Getting Hy on Python
    https://lwn.net/Articles/596626/
  68. Programming Can Be Fun with Hy
    https://opensourceforu.com/2014/02/pro­gramming-can-fun-hy/
  69. Přednáška o projektu Hy (pětiminutový lighttalk)
    http://blog.pault.ag/day/2013/04/02
  70. Hy (Wikipedia)
    https://en.wikipedia.org/wiki/Hy
  71. GNU Emacs Lisp Reference Manual: Point
    https://www.gnu.org/softwa­re/emacs/manual/html_node/e­lisp/Point.html
  72. GNU Emacs Lisp Reference Manual: Narrowing
    https://www.gnu.org/softwa­re/emacs/manual/html_node/e­lisp/Narrowing.html
  73. GNU Emacs Lisp Reference Manual: Functions that Create Markers
    https://www.gnu.org/softwa­re/emacs/manual/html_node/e­lisp/Creating-Markers.html
  74. GNU Emacs Lisp Reference Manual: Motion
    https://www.gnu.org/softwa­re/emacs/manual/html_node/e­lisp/Motion.html#Motion
  75. GNU Emacs Lisp Reference Manual: Basic Char Syntax
    https://www.gnu.org/softwa­re/emacs/manual/html_node/e­lisp/Basic-Char-Syntax.html
  76. Elisp: Sequence: List, Array
    http://ergoemacs.org/emac­s/elisp_list_vs_vector.html
  77. Elisp: Property List
    http://ergoemacs.org/emac­s/elisp_property_list.html
  78. Elisp: Hash Table
    http://ergoemacs.org/emac­s/elisp_hash_table.html
  79. Elisp: Association List
    http://ergoemacs.org/emac­s/elisp_association_list.html
  80. The mapcar Function (An Introduction to Programming in Emacs Lisp)
    https://www.gnu.org/softwa­re/emacs/manual/html_node/e­intr/mapcar.html
  81. Anaphoric macro
    https://en.wikipedia.org/wi­ki/Anaphoric_macro
  82. Some Common Lisp Loop Macro Examples
    https://www.youtube.com/wat­ch?v=3yl8o6r_omw
  83. A Guided Tour of Emacs
    https://www.gnu.org/softwa­re/emacs/tour/
  84. The Roots of Lisp
    http://www.paulgraham.com/ro­otsoflisp.html
  85. Evil (Emacs Wiki)
    https://www.emacswiki.org/emacs/Evil
  86. Evil (na GitHubu)
    https://github.com/emacs-evil/evil
  87. Evil (na stránkách repositáře MELPA)
    https://melpa.org/#/evil
  88. Evil Mode: How I Switched From VIM to Emacs
    https://blog.jakuba.net/2014/06/23/e­vil-mode-how-to-switch-from-vim-to-emacs.html
  89. GNU Emacs (home page)
    https://www.gnu.org/software/emacs/
  90. GNU Emacs (texteditors.org)
    http://texteditors.org/cgi-bin/wiki.pl?GnuEmacs
  91. An Introduction To Using GDB Under Emacs
    http://tedlab.mit.edu/~dr/gdbin­tro.html
  92. An Introduction to Programming in Emacs Lisp
    https://www.gnu.org/softwa­re/emacs/manual/html_node/e­intr/index.html
  93. 27.6 Running Debuggers Under Emacs
    https://www.gnu.org/softwa­re/emacs/manual/html_node/e­macs/Debuggers.html
  94. GdbMode
    http://www.emacswiki.org/e­macs/GdbMode
  95. Emacs (Wikipedia)
    https://en.wikipedia.org/wiki/Emacs
  96. Emacs timeline
    http://www.jwz.org/doc/emacs-timeline.html
  97. Emacs Text Editors Family
    http://texteditors.org/cgi-bin/wiki.pl?EmacsFamily
  98. Vrapper aneb spojení možností Vimu a Eclipse
    https://mojefedora.cz/vrapper-aneb-spojeni-moznosti-vimu-a-eclipse/
  99. 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/
  100. Emacs/Evil-mode – A basic reference to using evil mode in Emacs
    http://www.aakarshnair.com/posts/emacs-evil-mode-cheatsheet
  101. From Vim to Emacs+Evil chaotic migration guide
    https://juanjoalvarez.net/es/de­tail/2014/sep/19/vim-emacsevil-chaotic-migration-guide/
  102. Introduction to evil-mode {video)
    https://www.youtube.com/wat­ch?v=PeVQwYUxYEg
  103. EINE (Emacs Wiki)
    http://www.emacswiki.org/emacs/EINE
  104. EINE (Texteditors.org)
    http://texteditors.org/cgi-bin/wiki.pl?EINE
  105. ZWEI (Emacs Wiki)
    http://www.emacswiki.org/emacs/ZWEI
  106. ZWEI (Texteditors.org)
    http://texteditors.org/cgi-bin/wiki.pl?ZWEI
  107. Zmacs (Wikipedia)
    https://en.wikipedia.org/wiki/Zmacs
  108. Zmacs (Texteditors.org)
    http://texteditors.org/cgi-bin/wiki.pl?Zmacs
  109. TecoEmacs (Emacs Wiki)
    http://www.emacswiki.org/e­macs/TecoEmacs
  110. Micro Emacs
    http://www.emacswiki.org/e­macs/MicroEmacs
  111. Micro Emacs (Wikipedia)
    https://en.wikipedia.org/wi­ki/MicroEMACS
  112. EmacsHistory
    http://www.emacswiki.org/e­macs/EmacsHistory
  113. Seznam editorů s ovládáním podobným Emacsu či kompatibilních s příkazy Emacsu
    http://www.finseth.com/emacs.html
  114. evil-numbers
    https://github.com/cofi/evil-numbers
  115. Debuggery a jejich nadstavby v Linuxu (1.část)
    http://fedora.cz/debuggery-a-jejich-nadstavby-v-linuxu/
  116. Debuggery a jejich nadstavby v Linuxu (2.část)
    http://fedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-2-cast/
  117. Debuggery a jejich nadstavby v Linuxu (3): Nemiver
    http://fedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-3-nemiver/
  118. Debuggery a jejich nadstavby v Linuxu (4): KDbg
    http://fedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-4-kdbg/
  119. 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/
  120. Org mode
    https://orgmode.org/
  121. The Org Manual
    https://orgmode.org/manual/index.html
  122. Kakoune (modální textový editor)
    http://kakoune.org/
  123. Vim-style keybinding in Emacs/Evil-mode
    https://gist.github.com/tro­yp/6b4c9e1c8670200c04c16036805773d8
  124. Emacs – jak začít
    http://www.abclinuxu.cz/clan­ky/navody/emacs-jak-zacit
  125. Programovací jazyk LISP a LISP machines
    https://www.root.cz/clanky/pro­gramovaci-jazyk-lisp-a-lisp-machines/
  126. Evil-surround
    https://github.com/emacs-evil/evil-surround
  127. Spacemacs
    http://spacemacs.org/
  128. Lisp: Common Lisp, Racket, Clojure, Emacs Lisp
    http://hyperpolyglot.org/lisp
  129. Common Lisp, Scheme, Clojure, And Elisp Compared
    http://irreal.org/blog/?p=725
  130. Does Elisp Suck?
    http://irreal.org/blog/?p=675
  131. Emacs pro mírně pokročilé (9): Elisp
    https://www.root.cz/clanky/emacs-elisp/
  132. If I want to learn lisp, are emacs and elisp a good choice?
    https://www.reddit.com/r/e­macs/comments/2m141y/if_i_wan­t_to_learn_lisp_are_emacs_an­d_elisp_a/
  133. Clojure(Script) Interactive Development Environment that Rocks!
    https://github.com/clojure-emacs/cider
  134. An Introduction to Emacs Lisp
    https://harryrschwartz.com/2014/04/08/an-introduction-to-emacs-lisp.html
  135. Emergency Elisp
    http://steve-yegge.blogspot.com/2008/01/emergency-elisp.html
  136. Lambda calculus
    https://en.wikipedia.org/wi­ki/Lambda_calculus
  137. John McCarthy's original LISP paper from 1959
    https://www.reddit.com/r/pro­gramming/comments/17lpz4/joh­n_mccarthys_original_lisp_pa­per_from_1959/
  138. Micro Manual LISP
    https://www.scribd.com/do­cument/54050141/Micro-Manual-LISP
  139. How Lisp Became God's Own Programming Language
    https://twobithistory.org/2018/10/14/lis­p.html
  140. History of Lisp
    http://jmc.stanford.edu/ar­ticles/lisp/lisp.pdf
  141. The Roots of Lisp
    http://languagelog.ldc.upen­n.edu/myl/llog/jmc.pdf
  142. Racket
    https://racket-lang.org/
  143. The Racket Manifesto
    http://felleisen.org/matthi­as/manifesto/
  144. MIT replaces Scheme with Python
    https://www.johndcook.com/blog/2009/03/26/mit-replaces-scheme-with-python/
  145. Adventures in Advanced Symbolic Programming
    http://groups.csail.mit.e­du/mac/users/gjs/6.945/
  146. Why MIT Switched from Scheme to Python (2009)
    https://news.ycombinator.com/i­tem?id=14167453
  147. Starodávná stránka XLispu
    http://www.xlisp.org/
  148. AutoLISP
    https://en.wikipedia.org/wi­ki/AutoLISP
  149. Seriál PicoLisp: minimalistický a výkonný interpret Lispu
    https://www.root.cz/serialy/picolisp-minimalisticky-a-vykonny-interpret-lispu/
  150. Common Lisp
    https://common-lisp.net/
  151. Getting Going with Common Lisp
    https://cliki.net/Getting%20Started
  152. Online Tutorial (Common Lisp)
    https://cliki.net/online%20tutorial
  153. Guile Emacs
    https://www.emacswiki.org/e­macs/GuileEmacs
  154. Guile Emacs History
    https://www.emacswiki.org/e­macs/GuileEmacsHistory
  155. Guile is a programming language
    https://www.gnu.org/software/guile/
  156. MIT Scheme
    http://groups.csail.mit.e­du/mac/projects/scheme/
  157. SIOD: Scheme in One Defun
    http://people.delphiforum­s.com/gjc//siod.html
  158. CommonLispForEmacs
    https://www.emacswiki.org/e­macs/CommonLispForEmacs
  159. Elisp: print, princ, prin1, format, message
    http://ergoemacs.org/emac­s/elisp_printing.html
  160. Special Forms in Lisp
    http://www.nhplace.com/ken­t/Papers/Special-Forms.html
  161. Basic Building Blocks in LISP
    https://www.tutorialspoin­t.com/lisp/lisp_basic_syn­tax.htm
  162. Introduction to LISP – University of Pittsburgh
    https://people.cs.pitt.edu/~mi­los/courses/cs2740/Lectures/Lis­pTutorial.pdf
  163. Why don't people use LISP
    https://forums.freebsd.org/threads/why-dont-people-use-lisp.24572/
  164. Structured program theorem
    https://en.wikipedia.org/wi­ki/Structured_program_the­orem
  165. Clojure: API Documentation
    https://clojure.org/api/api
  166. Tutorial for the Common Lisp Loop Macro
    http://www.ai.sri.com/pkarp/loop.html
  167. Common Lisp's Loop Macro Examples for Beginners
    http://www.unixuser.org/~e­uske/doc/cl/loop.html
  168. A modern list api for Emacs. No 'cl required.
    https://github.com/magnars/dash.el
  169. The LOOP Facility
    http://www.lispworks.com/do­cumentation/HyperSpec/Body/06_a­.htm
  170. Clojure.org: Vars and the Global Environment
    http://clojure.org/Vars
  171. Clojure.org: Refs and Transactions
    http://clojure.org/Refs
  172. Clojure.org: Atoms
    http://clojure.org/Atoms
  173. Clojure.org: Agents as Asynchronous Actions
    http://clojure.org/agents
  174. Transient Data Structureshttp://clojure.or­g/transients
  175. Dynamic Languages Strike Back
    http://steve-yegge.blogspot.cz/2008/05/dynamic-languages-strike-back.html
  176. Scripting: Higher Level Programming for the 21st Century
    http://www.tcl.tk/doc/scripting.html
  177. Clojure (na Wikipedia EN)
    http://en.wikipedia.org/wiki/Clojure
  178. Clojure (na Wikipedia CS)
    http://cs.wikipedia.org/wiki/Clojure
  179. SICP (The Structure and Interpretation of Computer Programs)
    http://mitpress.mit.edu/sicp/
  180. Pure function
    http://en.wikipedia.org/wi­ki/Pure_function
  181. Funkcionální programování
    http://cs.wikipedia.org/wi­ki/Funkcionální_programová­ní
  182. 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/
  183. Pixie: lehký skriptovací jazyk s „kouzelnými“ schopnostmi
    https://www.root.cz/clanky/pixie-lehky-skriptovaci-jazyk-s-kouzelnymi-schopnostmi/
  184. Programovací jazyk Pixie: funkce ze základní knihovny a použití FFI
    https://www.root.cz/clanky/pro­gramovaci-jazyk-pixie-funkce-ze-zakladni-knihovny-a-pouziti-ffi/
  185. Stránka projektu Jython
    http://www.jython.org/
  186. Jython (Wikipedia)
    https://en.wikipedia.org/wiki/Jython
  187. Scripting for the Java Platform (Wikipedia)
    https://en.wikipedia.org/wi­ki/Scripting_for_the_Java_Plat­form
  188. JSR 223: Scripting for the JavaTM Platform
    https://jcp.org/en/jsr/detail?id=223
  189. List of JVM languages
    https://en.wikipedia.org/wi­ki/List_of_JVM_languages
  190. The JavaTM Virtual Machine Specification, Second Edition
    http://java.sun.com/docs/bo­oks/jvms/second_edition/html/VMSpec­TOC.doc.html
  191. The class File Format
    http://java.sun.com/docs/bo­oks/jvms/second_edition/html/Clas­sFile.doc.html
  192. javap – The Java Class File Disassembler
    http://docs.oracle.com/ja­vase/1.4.2/docs/tooldocs/win­dows/javap.html
  193. javap-java-1.6.0-openjdk(1) – Linux man page
    http://linux.die.net/man/1/javap-java-1.6.0-openjdk
  194. Using javap
    http://www.idevelopment.in­fo/data/Programming/java/mis­cellaneous_java/Using_javap­.html
  195. Examine class files with the javap command
    http://www.techrepublic.com/ar­ticle/examine-class-files-with-the-javap-command/5815354