Programovací jazyk Clojure a knihovny pro práci s vektory a maticemi (2)

Pavel Tišnovský 5. 5. 2015

Ve druhé části článku o knihovnách určených pro práci s vektory a maticemi v programovacím jazyku Clojure dokončíme popis knihovny nazvané core.matrix. Řekneme si mj. i o několika způsobech ukládání vektorů a matic v operační paměti i o použití takzvaných pohledů (views).

Obsah

1. Práce s vektory v knihovně core.matrix

2. Pomocná makra pro výpis prováděné operace i jejího výsledku

3. Operátory

4. Funkce pracující s vektory

5. Skalární a vektorový součin

6. Immutable vs. mutable vektory

7. Úplný zdrojový kód prvního demonstračního příkladu

8. Práce s maticemi v knihovně core.matrix

9. Neměnitelné matice

10. Měnitelné matice

11. Řídké matice, interní reprezentace matic

12. Pohledy (views)

13. Úplný zdrojový kód druhého demonstračního příkladu

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

15. Odkazy na předchozí části seriálu

16. Odkazy na Internetu

1. Práce s vektory v knihovně core.matrix

V první části článku o knihovnách určených pro práci s vektory a maticemi v programovacím jazyku Clojure jsme si nejprve řekli základní informace o významu termínu array programming, což je (poněkud zjednodušeně řečeno) jedno z paradigmat programování reprezentované především programovacími jazyky APL, J a částečně i MATLAB. Dále jsme se zabývali popisem některých funkcí a maker nabízených knihovnou nazvanou core.matrix. Dnes toto téma dokončíme, protože si na trojici demonstračních příkladů ukážeme další makra a funkce, které jsou touto knihovnou nabízeny. Kromě toho si taktéž řekneme, jakým způsobem mohou být vektory a především pak matice reprezentovány v operační paměti počítače a jak se v knihovně core.matrix pracuje s takzvanými pohledy (views), díky nimž je možné relativně snadno sdílet data několika matic v jediné struktuře.

Začneme popisem práce s vektory. Ty je možné v knihovně core.matrix vytvářet několika způsoby. Pokud se jedná o vektor obsahující samé nuly, lze takovou strukturu vytvořit funkcí zero-vector, které se jako jediný parametr předá délka vektoru. Pro vektory, jejichž prvky mají odlišnou hodnotu, se používá funkce array, které lze předat buď již existující vektor, sekvenci (mj. tedy i vektor v chápání jazyka Clojure a taktéž seznam) či řez 2D polem. Existuje ještě třetí možnost vytvoření vektoru, a to s využitím funkce to-vector, která slouží k transformaci libovolné matice na vektor obsahující všechny prvky původní matice (podobně nazvaná funkce as-vector bude popsána dále v sekci o pohledech):

# Funkce Výsledek Popis
1 array vektor vytvoření nového vektoru (nebo matice)
2 to-vector vektor vytvoření nového vektoru z již existující matice

2. Pomocné makro pro výpis prováděné operace i jejího výsledku

V trojici demonstračních příkladů se budou pro výpis prováděných operací i výsledků těchto operací používat dvě makra nazvaná print-vector a print-matrix. Funkce těchto maker je relativně jednoduchá – nejprve se vypíše úplný (nevyhodnocený) tvar výrazu, s nímž je makro zavoláno a posléze se s využitím funkce pm (pretty print matrix) vypíše vyhodnocený výraz, což může být skalár, vektor či matice. V případě matice je vhodnější použít makro print-matrix, které vypíše již první řádek matice na nový textový řádek, což zvyšuje čitelnost výstupu:

(defmacro print-vector
    "Makro, které na standardní výstup vypíše výraz a následně i
     hodnotu tohoto výrazu s použitím funkce pm (pretty print matrix).
     Předpokládá se, že se výraz vyhodnotí na vektor."
    [expression]
    `(do (printf "    %-32s =  " '~expression)
         (pm ~expression)))
(defmacro print-matrix
    "Makro, které na standardní výstup vypíše výraz a následně i
     hodnotu tohoto výrazu s použitím funkce pm (pretty print matrix).
     Předpokládá se, že se výraz vyhodnotí na matici"
    [expression]
    `(do (println "   " '~expression "=")
         (pm ~expression)
         (println)))

Význam znaků se speciálním významem, které lze používat v makrech, naleznete v následující tabulce:

# Symbol v makru Název Význam
1 ` syntax-quote provádí plnou kvalifikaci symbolů + zde lze použít makra ~ a ~@
2 ~ unquote zajistí, že se vyhodnotí pouze označená část formy (= provede substituci této části výsledkem)
3 ~@ unquote-splicing podobné předchozími makru, ovšem výsledná sekvence se vloží ve formě samostatných prvků do „obalující“ sekvence

Bližší informace o tvorbě a použití maker lze najít v těchto článcích:

  1. Programovací jazyk Clojure 14: základy práce se systémem maker
    http://www.root.cz/clanky/programovaci-jazyk-clojure-14-zaklady-prace-se-systemem-maker/
  2. Programovací jazyk Clojure 15: tvorba uživatelských maker
    http://www.root.cz/clanky/programovaci-jazyk-clojure-15-tvorba-uzivatelskych-maker/
  3. Programovací jazyk Clojure 16: složitější uživatelská makra
    http://www.root.cz/clanky/programovaci-jazyk-clojure-16-slozitejsi-uzivatelska-makra/
  4. Programovací jazyk Clojure 17: využití standardních maker v praxi
    http://www.root.cz/clanky/programovaci-jazyk-clojure-17-vyuziti-standardnich-maker-v-praxi/

3. Operátory

Ve jmenném prostoru clojure.core.matrix.operators jsou deklarovány funkce pojmenované +, -, *, /, ** a ==, které lze v programovacím jazyku Clojure považovat za obdobu klasických operátorů. Tyto funkce/operátory jsou běžně deklarovány pro celá čísla, reálná čísla i čísla racionální (zlomky), ovšem právě díky redeklaraci těchto funkcí v clojure.core.matrix.operators je lze použít i pro operace prováděné nad vektory a maticemi. V případě vektorů jsou tyto operace deklarovány takovým způsobem, že jsou prováděny vždy na prvky vektorů se shodným indexem a pokud je jedním z operandů skalární hodnota a nikoli vektor, je skalár převeden na vektor stejné délky jakou má druhý operand. Toto „rozšíření“ skalární hodnoty na vektor se nazývá broadcast a podobně funguje i „rozšíření“ vektoru na matici (viz též další kapitoly). Podívejme se nyní na způsob použití výše jmenovaných funkcí:

(defn immutable-vector-operators
    "Test chování operátorů předefinovaných ve jmenném prostoru
     clojure.core.matrix.operators."
    [vector1 vector2]
    (println "*** Test základních operátorů aplikovaných na vektory ***")
    (println "Vstupní data (vektory):")
    (print-vector vector1)
    (print-vector vector2)
    (println "Vyhodnocení:")
    ; operace provedené mezi vektorem a skalární hodnotou
    (print-vector (+ vector1 10))
    (print-vector (- vector1 10))
    (print-vector (* vector1 10))
    (print-vector (/ vector1 10))
    ; operace provedené mezi skalární hodnotou a vektorem
    (print-vector (+ 10 vector1))
    (print-vector (- 10 vector1))
    (print-vector (* 10 vector1))
    (print-vector (/ 10 vector1))
    ; operace provedené mezi dvěma vektory (zde stejné délky)
    (print-vector (+ vector1 vector2))
    (print-vector (- vector1 vector2))
    (print-vector (* vector1 vector2))
    (print-vector (/ vector1 vector2))
    (println))
 
(defn immutable-vector-tests
    "Spustí všechny testovací funkce s neměnitelnými vektory."
    []
    (let [v1 (array [1 2 3])
          v2 (array [2 3 4])]
          (immutable-vector-operators v1 v2)
          (immutable-vector-special-operators v1 v2)

Na standardním výstupu by se měly objevit tyto řádky:

*** Test základních operátorů aplikovaných na vektory ***
Vstupní data (vektory):
    vector1                          =  [1.000 2.000 3.000]
    vector2                          =  [2.000 3.000 4.000]
Vyhodnocení:
    (+ vector1 10)                   =  [11.000 12.000 13.000]
    (- vector1 10)                   =  [-9.000 -8.000 -7.000]
    (* vector1 10)                   =  [10.000 20.000 30.000]
    (/ vector1 10)                   =  [0.100 0.200 0.300]
    (+ 10 vector1)                   =  [11.000 12.000 13.000]
    (- 10 vector1)                   =  [9.000 8.000 7.000]
    (* 10 vector1)                   =  [10.000 20.000 30.000]
    (/ 10 vector1)                   =  [10.000 5.000 3.333]
    (+ vector1 vector2)              =  [3.000 5.000 7.000]
    (- vector1 vector2)              =  [-1.000 -1.000 -1.000]
    (* vector1 vector2)              =  [2.000 6.000 12.000]
    (/ vector1 vector2)              =  [0.500 0.667 0.750]

Podobným způsobem jsou předeklarovány i funkce/operátory ** (umocnění) a == (porovnání prvek po prvku):

(defn immutable-vector-special-operators
    "Test chování speciálních operátorů předefinovaných
     ve jmenném prostoru clojure.core.matrix.operators."
    [vector1 vector2]
    (println "*** Test speciálních operátorů aplikovaných na vektory ***")
    (println "Vstupní data (vektory):")
    (print-vector vector1)
    (print-vector vector2)
    (println "Vyhodnocení:")
    ; operace provedené mezi vektorem a skalární hodnotou
    (print-vector (** vector1 10))
    ; operace provedené mezi skalární hodnotou a vektorem
    (print-vector (** 10 vector1))
    ; operace provedené mezi dvěma vektory (zde stejné délky)
    (print-vector (** vector1 vector2))
    ; operátor ekvivalence
    (print-vector (== vector1 vector2))
    (print-vector (== vector1 vector1))
    (print-vector (== vector1 (array [1 2 3])))
    (print-vector (== vector1 [1 2 3]))
    (print-vector (== vector1 '(1 2 3)))
    (println))

Na standardním výstupu by se měly objevit tyto řádky:

*** Test speciálních operátorů aplikovaných na vektory ***
Vstupní data (vektory):
    vector1                          =  [1.000 2.000 3.000]
    vector2                          =  [2.000 3.000 4.000]
Vyhodnocení:
    (** vector1 10)                  =  [1.000 1024.000 59049.000]
    (** 10 vector1)                  =  [10.000 100.000 1000.000]
    (** vector1 vector2)             =  [1.000 8.000 81.000]
    (== vector1 vector2)             =  false
    (== vector1 vector1)             =  true
    (== vector1 (array [1 2 3]))     =  true
    (== vector1 [1 2 3])             =  true
    (== vector1 (quote (1 2 3)))     =  true

4. Funkce pracující s vektory

V knihovně core.matrix je deklarováno velké množství funkcí, které dokážou pracovat s celými vektory popř. s jednotlivými prvky, z nichž se vektory skládají. Některé z těchto funkcí jsme si již popsali minule, ovšem ve skutečnosti těchto funkcí existuje mnohem větší množství, jak bude ostatně patrné i z úryvku demonstračního příkladu:

(defn immutable-vector-functions
    "Test chování vybraných funkcí, které lze nalézt ve jmenných prostorech
     clojure.core.matrix a clojure.core.matrix.operators."
    [vector1 vector2 vector3]
    (println "*** Test funkcí aplikovaných na vektory ***")
    (println "Vstupní data (vektory):")
    (print-vector vector1)
    (print-vector vector2)
    (print-vector vector3)
    (println "Vyhodnocení:")
    (print-vector (emin vector1))
    (print-vector (emin vector2))
    (print-vector (emin vector3))
    (print-vector (emax vector1))
    (print-vector (emax vector2))
    (print-vector (emax vector3))
    (print-vector (normalise vector1))
    (print-vector (normalise vector2))
    (print-vector (normalise vector3))
    (print-vector (length vector1))
    (print-vector (length vector2))
    (print-vector (length vector3))
    (print-vector (length-squared vector1))
    (print-vector (length-squared vector2))
    (print-vector (length-squared vector3))
    (print-vector (distance vector1 vector2))
    (print-vector (distance vector1 vector3))
    (print-vector (distance vector2 vector3))
    (print-vector (join vector1 vector2))
    (print-vector (join vector1 vector2 vector3))
    (println))

Nejprve se podívejme na výsledky vypsané po spuštění této funkce:

*** Test funkcí aplikovaných na vektory ***
Vstupní data (vektory):
    vector1                          =  [1.000 2.000 3.000]
    vector2                          =  [2.000 3.000 4.000]
    vector3                          =  [1.000 1.000]
Vyhodnocení:
    (emin vector1)                   =  1.000
    (emin vector2)                   =  2.000
    (emin vector3)                   =  1.000
    (emax vector1)                   =  3.000
    (emax vector2)                   =  4.000
    (emax vector3)                   =  1.000
    (normalise vector1)              =  [0.267 0.535 0.802]
    (normalise vector2)              =  [0.371 0.557 0.743]
    (normalise vector3)              =  [0.707 0.707]
    (length vector1)                 =  3.742
    (length vector2)                 =  5.385
    (length vector3)                 =  1.414
    (length-squared vector1)         =  14.000
    (length-squared vector2)         =  29.000
    (length-squared vector3)         =  2.000
    (distance vector1 vector2)       =  1.732
    (distance vector1 vector3)       =  1.000
    (distance vector2 vector3)       =  2.236
    (join vector1 vector2)           =  [1.000 2.000 3.000 2.000 3.000 4.000]
    (join vector1 vector2 vector3)   =  [1.000 2.000 3.000 2.000 3.000 4.000 1.000 1.000]

Význam jednotlivých funkcí použitých v předchozím příkladu je vysvětlen v tabulce:

# Funkce Výsledek Popis
1 emin skalár nalezne nejmenší prvek vektoru
2 emax skalár nalezne největší prvek vektoru
3 normalize vektor normalizace vektoru (na délku rovnou jedné)
4 length skalár výpočet délky vektoru
5 length-squared skalár výpočet druhé mocniny délky vektoru
6 distance skalár dva vektory jsou chápány jako 2D/3D body, vypočítá se jejich vzdálenost
7 join vektor spojení dvou čí většího množství vektorů

Poznámka: žádná z těchto funkcí nemodifikuje původní vektor, vždy je vytvořena nová datová struktura.

5. Skalární a vektorový součin

Ve třetí kapitole jsme se mj. zmínili i o funkci/operátoru *, který slouží k vynásobení těch prvků vektorů, které mají shodný index, tj. první prvek vektoru číslo 1 je vynásoben s prvním prvkem vektoru číslo 2 atd. Kromě toho se však v mnoha disciplínách (mj. i v počítačové grafice) pracuje se skalárním a vektorovým součinem. I tyto operace jsou samozřejmě v knihovně core.matrix podporovány:

# Funkce Výsledek Popis
1 dot skalár skalární součin dvou vektorů
2 inner-product skalár zobecnění skalárního součinu (pro matice má význam násobení matic)
3 cross vektor vektorový součin dvou vektorů

Opět se podívejme na způsob použití těchto funkcí:

(defn immutable-vector-products
    "Skalární a vektorový součin dvou trojsložkových vektorů."
    [vector1 vector2]
    (println "*** Skalární a vektorový součin dvou trojsložkových vektorů ***")
    (println "Vstupní data (vektory):")
    (print-vector vector1)
    (print-vector vector2)
    (println "Vyhodnocení:")
    (print-vector (dot vector1 vector1))
    (print-vector (dot vector1 vector2))
    (print-vector (dot vector2 vector1))
    (print-vector (dot vector2 vector2))
    (print-vector (inner-product vector1 vector1))
    (print-vector (inner-product vector1 vector2))
    (print-vector (inner-product vector2 vector1))
    (print-vector (inner-product vector2 vector2))
    (print-vector (cross vector1 vector1))
    (print-vector (cross vector1 vector2))
    (print-vector (cross vector2 vector1))
    (print-vector (cross vector2 vector2))
    (println))
 
(defn immutable-vector-tests
    "Spustí všechny testovací funkce s neměnitelnými vektory."
    []
    (let [v0 (zero-vector 3)
          v1 (array [1 2 3])
          v2 (array [2 3 4])
          v3 (array [1 1])]
          (immutable-vector-products v1 v2)
          (immutable-vector-products v0 v1)))

Výsledek zavolání testovací funkce:

*** Skalární a vektorový součin dvou trojsložkových vektorů ***
Vstupní data (vektory):
    vector1                          =  [1.000 2.000 3.000]
    vector2                          =  [2.000 3.000 4.000]
Vyhodnocení:
    (dot vector1 vector1)            =  14.000
    (dot vector1 vector2)            =  20.000
    (dot vector2 vector1)            =  20.000
    (dot vector2 vector2)            =  29.000
    (inner-product vector1 vector1)  =  14.000
    (inner-product vector1 vector2)  =  20.000
    (inner-product vector2 vector1)  =  20.000
    (inner-product vector2 vector2)  =  29.000
    (cross vector1 vector1)          =  [0.000 0.000 0.000]
    (cross vector1 vector2)          =  [-1.000 2.000 -1.000]
    (cross vector2 vector1)          =  [1.000 -2.000 1.000]
    (cross vector2 vector2)          =  [0.000 0.000 0.000]
 
*** Skalární a vektorový součin dvou trojsložkových vektorů ***
Vstupní data (vektory):
    vector1                          =  [0.000 0.000 0.000]
    vector2                          =  [1.000 2.000 3.000]
Vyhodnocení:
    (dot vector1 vector1)            =  0.000
    (dot vector1 vector2)            =  0.000
    (dot vector2 vector1)            =  0.000
    (dot vector2 vector2)            =  14.000
    (inner-product vector1 vector1)  =  0.000
    (inner-product vector1 vector2)  =  0.000
    (inner-product vector2 vector1)  =  0.000
    (inner-product vector2 vector2)  =  14.000
    (cross vector1 vector1)          =  [0.000 0.000 0.000]
    (cross vector1 vector2)          =  [0.000 0.000 0.000]
    (cross vector2 vector1)          =  [0.000 0.000 0.000]
    (cross vector2 vector2)          =  [0.000 0.000 0.000]

6. Immutable vs. mutable vektory

Všechny funkce pro práci s vektory, které jsme si doposud popsali, byly založeny na myšlence, že všechny datové struktury by měly být v ideálním případě neměnitelné neboli immutable. V případě funkcionálních jazyků a samozřejmě též i programovacího jazyka Clojure se názvem neměnitelnost myslí jak neměnitelnost samotné struktury tak i neměnitelnost dat ve struktuře uložených, ovšem u jiných jazyků může mít tento význam odlišný význam (v Javě označuje pouze neměnitelnost samotné struktury, ale nikoli již uložených dat – příkladem je běžné pole).

Vraťme se však k vektorům a knihovně core.matrix. Kromě čistě funkcionálně navržených funkcí popsaných výše zde jsou deklarovány i funkce měnící původní vektor, který však musí být vytvořen pomocí funkce mutable. Všechny funkce měnící původní vektor mají na konci svého jména znak !, což odpovídá konvencím Clojure. Výjimkou je čtveřice „operátorů“ +=, -=, *= a div=, kde sice znak ! není použit, ale význam operace je zřejmý – výsledek se uloží do prvního operandu:

# Funkce Výsledek Popis
1 += vektor stejný význam jako + ovšem výsledek se uloží do prvního vektoru
2 -= vektor stejný význam jako – ovšem výsledek se uloží do prvního vektoru
3 *= vektor stejný význam jako * ovšem výsledek se uloží do prvního vektoru
4 div= vektor stejný význam jako / ovšem výsledek se uloží do prvního vektoru
5 fill! vektor naplnění vektoru specifikovanou hodnotou
6 sin! vektor aplikace funkce sin na všechny prvky vektoru
7 abs! vektor výpočet absolutní hodnoty všech prvků vektoru
8 cross! vektor výpočet vektorového součinu
9 mset! vektor změna hodnoty vybraného prvku vektoru

Podívejme se nyní na trojici funkcí, v nichž se pracuje s měnitelnými vektory (povšimněte si volání mutable v poslední funkci při konstrukci vektorů):

(defn mutable-vector-operators
    "Test chování operátorů předefinovaných ve jmenném prostoru
     clojure.core.matrix.operators na měnitelné vektory."
    [vector1 vector2]
    (println "*** Test základních operátorů aplikovaných na měnitelné vektory ***")
    (println "Vstupní data (vektory):")
    (print-vector vector1)
    (print-vector vector2)
    (println "Vyhodnocení:")
    ; operace provedené mezi dvěma vektory (zde stejné délky)
    (print-vector (+= vector1 vector2))
    (print-vector (-= vector1 vector2))
    (print-vector (*= vector1 vector2))
    (print-vector (div= vector1 vector2))
    (println))
 
(defn mutable-vector-functions
    "Test chování vybraných funkcí, které lze nalézt ve jmenných prostorech
     clojure.core.matrix a clojure.core.matrix.operators na měnitelné vektory."
    [vector1 vector2 vector3]
    (println "*** Test funkcí aplikovaných na měnitelné vektory ***")
    (println "Vstupní data (vektory):")
    (print-vector vector1)
    (print-vector vector2)
    (print-vector vector3)
    (println "Vyhodnocení:")
    (print-vector (sin! vector1))
    (print-vector (sin! vector2))
    (print-vector (sin! vector3))
    (print-vector (fill! vector1 1))
    (print-vector (fill! vector2 2))
    (print-vector (fill! vector3 3))
    (println))
 
(defn mutable-vector-tests
    []
    (let [v0 (mutable (zero-vector 3))
          v1 (mutable (array [1 2 3]))
          v2 (mutable (array [2 3 4]))]
          (mutable-vector-operators v1 v2)
          (mutable-vector-functions v0 v1 v2)))

Při zavolání funkce mutable-vector-tests by se na standardní výstup měly vypsat následující řádky:

*** Test základních operátorů aplikovaných na měnitelné vektory ***
Vstupní data (vektory):
    vector1                          =  [1.000 2.000 3.000]
    vector2                          =  [2.000 3.000 4.000]
Vyhodnocení:
    (+= vector1 vector2)             =  [3.000 5.000 7.000]
    (-= vector1 vector2)             =  [1.000 2.000 3.000]
    (*= vector1 vector2)             =  [2.000 6.000 12.000]
    (div= vector1 vector2)           =  [1.000 2.000 3.000]
 
*** Test funkcí aplikovaných na měnitelné vektory ***
Vstupní data (vektory):
    vector1                          =  [0.000 0.000 0.000]
    vector2                          =  [1.000 2.000 3.000]
    vector3                          =  [2.000 3.000 4.000]
Vyhodnocení:
    (sin! vector1)                   =  [0.000 0.000 0.000]
    (sin! vector2)                   =  [0.841 0.909 0.141]
    (sin! vector3)                   =  [0.909 0.141 -0.757]
    (fill! vector1 1)                =  [1.000 1.000 1.000]
    (fill! vector2 2)                =  [2.000 2.000 2.000]
    (fill! vector3 3)                =  [3.000 3.000 3.000]

Povšimněte si, že návratová hodnota všech použitých funkcí je shodná s hodnotou ukládanou do prvního (měnitelného) vektoru.

7. Úplný zdrojový kód prvního demonstračního příkladu

V této kapitole bude uveden výpis úplného zdrojového kódu prvního demonstračního příkladu, z něhož jsme používali úryvky v předchozích kapitolách:

project.clj:

(defproject vector1 "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.6.0"]
                 [net.mikera/core.matrix "0.34.0"]]
  :main ^:skip-aot vector1.core
  :target-path "target/%s"
  :profiles {:uberjar {:aot :all}})

core.clj:

(ns vector1.core
  (:gen-class))
 
(use 'clojure.core.matrix)
(use 'clojure.core.matrix.operators)
 
(defmacro print-vector
    "Makro, které na standardní výstup vypíše výraz a následně i
     hodnotu tohoto výrazu s použitím funkce pm (pretty print matrix).
     Předpokládá se, že se výraz vyhodnotí na vektor."
    [expression]
    `(do (printf "    %-32s =  " '~expression)
         (pm ~expression)))
 
(defn immutable-vector-operators
    "Test chování operátorů předefinovaných ve jmenném prostoru
     clojure.core.matrix.operators."
    [vector1 vector2]
    (println "*** Test základních operátorů aplikovaných na vektory ***")
    (println "Vstupní data (vektory):")
    (print-vector vector1)
    (print-vector vector2)
    (println "Vyhodnocení:")
    ; operace provedené mezi vektorem a skalární hodnotou
    (print-vector (+ vector1 10))
    (print-vector (- vector1 10))
    (print-vector (* vector1 10))
    (print-vector (/ vector1 10))
    ; operace provedené mezi skalární hodnotou a vektorem
    (print-vector (+ 10 vector1))
    (print-vector (- 10 vector1))
    (print-vector (* 10 vector1))
    (print-vector (/ 10 vector1))
    ; operace provedené mezi dvěma vektory (zde stejné délky)
    (print-vector (+ vector1 vector2))
    (print-vector (- vector1 vector2))
    (print-vector (* vector1 vector2))
    (print-vector (/ vector1 vector2))
    (println))
 
(defn immutable-vector-special-operators
    "Test chování speciálních operátorů předefinovaných
     ve jmenném prostoru clojure.core.matrix.operators."
    [vector1 vector2]
    (println "*** Test speciálních operátorů aplikovaných na vektory ***")
    (println "Vstupní data (vektory):")
    (print-vector vector1)
    (print-vector vector2)
    (println "Vyhodnocení:")
    ; operace provedené mezi vektorem a skalární hodnotou
    (print-vector (** vector1 10))
    ; operace provedené mezi skalární hodnotou a vektorem
    (print-vector (** 10 vector1))
    ; operace provedené mezi dvěma vektory (zde stejné délky)
    (print-vector (** vector1 vector2))
    ; operátor ekvivalence
    (print-vector (== vector1 vector2))
    (print-vector (== vector1 vector1))
    (print-vector (== vector1 (array [1 2 3])))
    (print-vector (== vector1 [1 2 3]))
    (print-vector (== vector1 '(1 2 3)))
    (println))
 
(defn immutable-vector-functions
    "Test chování vybraných funkcí, které lze nalézt ve jmenných prostorech
     clojure.core.matrix a clojure.core.matrix.operators."
    [vector1 vector2 vector3]
    (println "*** Test funkcí aplikovaných na vektory ***")
    (println "Vstupní data (vektory):")
    (print-vector vector1)
    (print-vector vector2)
    (print-vector vector3)
    (println "Vyhodnocení:")
    (print-vector (emin vector1))
    (print-vector (emin vector2))
    (print-vector (emin vector3))
    (print-vector (emax vector1))
    (print-vector (emax vector2))
    (print-vector (emax vector3))
    (print-vector (normalise vector1))
    (print-vector (normalise vector2))
    (print-vector (normalise vector3))
    (print-vector (length vector1))
    (print-vector (length vector2))
    (print-vector (length vector3))
    (print-vector (length-squared vector1))
    (print-vector (length-squared vector2))
    (print-vector (length-squared vector3))
    (print-vector (distance vector1 vector2))
    (print-vector (distance vector1 vector3))
    (print-vector (distance vector2 vector3))
    (print-vector (join vector1 vector2))
    (print-vector (join vector1 vector2 vector3))
    (println))
 
(defn immutable-vector-products
    "Skalární a vektorový součin dvou trojsložkových vektorů."
    [vector1 vector2]
    (println "*** Skalární a vektorový součin dvou trojsložkových vektorů ***")
    (println "Vstupní data (vektory):")
    (print-vector vector1)
    (print-vector vector2)
    (println "Vyhodnocení:")
    (print-vector (dot vector1 vector1))
    (print-vector (dot vector1 vector2))
    (print-vector (dot vector2 vector1))
    (print-vector (dot vector2 vector2))
    (print-vector (inner-product vector1 vector1))
    (print-vector (inner-product vector1 vector2))
    (print-vector (inner-product vector2 vector1))
    (print-vector (inner-product vector2 vector2))
    (print-vector (cross vector1 vector1))
    (print-vector (cross vector1 vector2))
    (print-vector (cross vector2 vector1))
    (print-vector (cross vector2 vector2))
    (println))
 
(defn immutable-vector-tests
    "Spustí všechny testovací funkce s neměnitelnými vektory."
    []
    (let [v0 (zero-vector 3)
          v1 (array [1 2 3])
          v2 (array [2 3 4])
          v3 (array [1 1])]
          (immutable-vector-operators v1 v2)
          (immutable-vector-special-operators v1 v2)
          (immutable-vector-functions v1 v2 v3)
          (immutable-vector-products v1 v2)
          (immutable-vector-products v0 v1)))
 
(defn mutable-vector-operators
    "Test chování operátorů předefinovaných ve jmenném prostoru
     clojure.core.matrix.operators na měnitelné vektory."
    [vector1 vector2]
    (println "*** Test základních operátorů aplikovaných na měnitelné vektory ***")
    (println "Vstupní data (vektory):")
    (print-vector vector1)
    (print-vector vector2)
    (println "Vyhodnocení:")
    ; operace provedené mezi dvěma vektory (zde stejné délky)
    (print-vector (+= vector1 vector2))
    (print-vector (-= vector1 vector2))
    (print-vector (*= vector1 vector2))
    (print-vector (div= vector1 vector2))
    (println))
 
(defn mutable-vector-functions
    "Test chování vybraných funkcí, které lze nalézt ve jmenných prostorech
     clojure.core.matrix a clojure.core.matrix.operators na měnitelné vektory."
    [vector1 vector2 vector3]
    (println "*** Test funkcí aplikovaných na měnitelné vektory ***")
    (println "Vstupní data (vektory):")
    (print-vector vector1)
    (print-vector vector2)
    (print-vector vector3)
    (println "Vyhodnocení:")
    (print-vector (sin! vector1))
    (print-vector (sin! vector2))
    (print-vector (sin! vector3))
    (print-vector (fill! vector1 1))
    (print-vector (fill! vector2 2))
    (print-vector (fill! vector3 3))
    (println))
 
(defn mutable-vector-tests
    []
    (let [v0 (mutable (zero-vector 3))
          v1 (mutable (array [1 2 3]))
          v2 (mutable (array [2 3 4]))]
          (mutable-vector-operators v1 v2)
          (mutable-vector-functions v0 v1 v2)))
 
(defn -main
    "Vítejte v Matrixu podruhé..."
    [& args]
    (immutable-vector-tests)
    (mutable-vector-tests))

8. Práce s maticemi v knihovně core.matrix

Nejdůležitější datovou strukturou, s níž se v knihovně core.matrix pracuje, jsou samozřejmě matice, především pak dvourozměrné matice. Tyto matice mohou mít libovolné rozměry a při jejich vytváření je možné si zvolit jak způsob jejich uložení v operační paměti počítače, tak i to, zda se bude jednat o matice měnitelné (mutable) či neměnitelné (immutable). Již v předchozí části tohoto seriálu jsme se seznámili s některými funkcemi používanými při práci s maticemi. V první řadě se jedná o funkce sloužící pro vytvoření matic různých typů, viz též následující tabulku:

# Funkce Výsledek Popis
1 matrix matice vytvoření matice a inicializace jejích prvků
1 zero-matrix matice vytvoření matice s nulovými prvky
1 identity-matrix matice vytvoření jednotkové matice
1 permutation-matrix matice vytvoření matice, v níž jsou jedničky umístěny podle specifikovaného vektoru

Další velmi důležitou funkcí je funkce reshape, která dokáže změnit velikost matice a vhodným způsobem přeorganizovat prvky v původní matici.

9. Neměnitelné matice

Podívejme se nyní na některé další funkce, které lze použít pro práci s maticemi. Nejprve se budeme zabývat již zmíněnými funkcemi/operátory, které mají u matic v některých případech odlišný význam, než tomu bylo u vektorů:

# Funkce Výsledek Popis
1 + matice součet matic (může se provést broadcast)
2 matice rozdíl matic (může se provést broadcast)
3 * matice maticový součin (nikoli prvek po prvku!)
4 / matice maticový součin s inverzní maticí

Chování těchto funkcí/operátorů si samozřejmě vyzkoušíme v praxi:

(defn immutable-matrix-tests
    "Spustí všechny testovací funkce s neměnitelnými maticemi"
    []
    (let [m0 (zero-matrix 3 3)
          m1 (identity-matrix 3 3)
          m2 (array [[1 2 3] [4 5 6] [7 8 9]])
          m3 (array [[0 1] [2 3]])]
          (immutable-matrix-operators m0 m1)
          (immutable-matrix-operators m1 m2)
          ;(immutable-matrix-special-operators m1 m2)  ; bude použito níže
          ;(immutable-matrix-functions m1 m2 m3)))     ; bude použito níže
(defn immutable-matrix-operators
    "Test chování operátorů předefinovaných ve jmenném prostoru
     clojure.core.matrix.operators."
    [matrix1 matrix2]
    (println "*** Test základních operátorů aplikovaných na matice ***")
    (println "Vstupní data (matice):")
    (print-matrix matrix1)
    (print-matrix matrix2)
    (println "Vyhodnocení:")
    ; operace provedené mezi maticí a skalární hodnotou
    (print-matrix (+ matrix1 10))
    (print-matrix (- matrix1 10))
    (print-matrix (* matrix1 10))
    (print-matrix (/ matrix1 10))
    ; operace provedené mezi skalární hodnotou a maticí
    (print-matrix (+ 10 matrix1))
    (print-matrix (- 10 matrix1))
    (print-matrix (* 10 matrix1))
    (try
        (print-matrix (/ 10 matrix1))
        (catch java.lang.ArithmeticException e
            (println "Exception: " (.toString e))))
    ; operace provedené mezi dvěma maticemi
    (print-matrix (+ matrix1 matrix2))
    (print-matrix (- matrix1 matrix2))
    (print-matrix (* matrix1 matrix2))
    (try
        (print-matrix (/ matrix1 matrix2))
        (catch java.lang.ArithmeticException e
            (println "Exception: " (.toString e))))
    (println))

Výsledky vypadají následovně (povšimněte si, že může dojít k vyhození výjimky při dělení nulou):

*** Test základních operátorů aplikovaných na matice ***
Vstupní data (matice):
    matrix1 =
[[0.000 0.000 0.000]
 [0.000 0.000 0.000]
 [0.000 0.000 0.000]]
 
    matrix2 =
[[1.000 0.000 0.000]
 [0.000 1.000 0.000]
 [0.000 0.000 1.000]]
 
Vyhodnocení:
    (+ matrix1 10) =
[[10.000 10.000 10.000]
 [10.000 10.000 10.000]
 [10.000 10.000 10.000]]
 
    (- matrix1 10) =
[[-10.000 -10.000 -10.000]
 [-10.000 -10.000 -10.000]
 [-10.000 -10.000 -10.000]]
 
    (* matrix1 10) =
[[0.000 0.000 0.000]
 [0.000 0.000 0.000]
 [0.000 0.000 0.000]]
 
    (/ matrix1 10) =
[[0.000 0.000 0.000]
 [0.000 0.000 0.000]
 [0.000 0.000 0.000]]
 
    (+ 10 matrix1) =
[[10.000 10.000 10.000]
 [10.000 10.000 10.000]
 [10.000 10.000 10.000]]
 
    (- 10 matrix1) =
[[10.000 10.000 10.000]
 [10.000 10.000 10.000]
 [10.000 10.000 10.000]]
 
    (* 10 matrix1) =
[[0.000 0.000 0.000]
 [0.000 0.000 0.000]
 [0.000 0.000 0.000]]
 
    (/ 10 matrix1) =
Exception:  java.lang.ArithmeticException: Divide by zero
 
    (+ matrix1 matrix2) =
[[1.000 0.000 0.000]
 [0.000 1.000 0.000]
 [0.000 0.000 1.000]]
 
    (- matrix1 matrix2) =
[[-1.000  0.000  0.000]
 [ 0.000 -1.000  0.000]
 [ 0.000  0.000 -1.000]]
 
    (* matrix1 matrix2) =
[[0.000 0.000 0.000]
 [0.000 0.000 0.000]
 [0.000 0.000 0.000]]
 
    (/ matrix1 matrix2) =
Exception:  java.lang.ArithmeticException: Divide by zero
 
*** Test základních operátorů aplikovaných na matice ***
Vstupní data (matice):
    matrix1 =
[[1.000 0.000 0.000]
 [0.000 1.000 0.000]
 [0.000 0.000 1.000]]
 
    matrix2 =
[[1.000 2.000 3.000]
 [4.000 5.000 6.000]
 [7.000 8.000 9.000]]
 
Vyhodnocení:
    (+ matrix1 10) =
[[11.000 10.000 10.000]
 [10.000 11.000 10.000]
 [10.000 10.000 11.000]]
 
    (- matrix1 10) =
[[ -9.000 -10.000 -10.000]
 [-10.000  -9.000 -10.000]
 [-10.000 -10.000  -9.000]]
 
    (* matrix1 10) =
[[10.000  0.000  0.000]
 [ 0.000 10.000  0.000]
 [ 0.000  0.000 10.000]]
 
    (/ matrix1 10) =
[[0.100 0.000 0.000]
 [0.000 0.100 0.000]
 [0.000 0.000 0.100]]
 
    (+ 10 matrix1) =
[[11.000 10.000 10.000]
 [10.000 11.000 10.000]
 [10.000 10.000 11.000]]
 
    (- 10 matrix1) =
[[ 9.000 10.000 10.000]
 [10.000  9.000 10.000]
 [10.000 10.000  9.000]]
 
    (* 10 matrix1) =
[[10.000  0.000  0.000]
 [ 0.000 10.000  0.000]
 [ 0.000  0.000 10.000]]
 
    (/ 10 matrix1) =
Exception:  java.lang.ArithmeticException: Divide by zero
 
    (+ matrix1 matrix2) =
[[2.000 2.000  3.000]
 [4.000 6.000  6.000]
 [7.000 8.000 10.000]]
 
    (- matrix1 matrix2) =
[[ 0.000 -2.000 -3.000]
 [-4.000 -4.000 -6.000]
 [-7.000 -8.000 -8.000]]
 
    (* matrix1 matrix2) =
[[1.000 0.000 0.000]
 [0.000 5.000 0.000]
 [0.000 0.000 9.000]]
 
    (/ matrix1 matrix2) =
[[1.000 0.000 0.000]
 [0.000 0.200 0.000]
 [0.000 0.000 0.111]]

Zajímavé je i chování operátorů ** (aplikován prvek po prvku s případným broadcastem) a == (taktéž aplikován prvek po prvku):

(defn immutable-matrix-special-operators
    "Test chování speciálních operátorů předefinovaných
     ve jmenném prostoru clojure.core.matrix.operators."
    [matrix1 matrix2]
    (println "*** Test speciálních operátorů aplikovaných na matice ***")
    (println "Vstupní data (matice):")
    (print-matrix matrix1)
    (print-matrix matrix2)
    (println "Vyhodnocení:")
    ; operace provedené mezi maticí a skalární hodnotou
    (print-matrix (** matrix1 10))
    ; operace provedené mezi skalární hodnotou a maticí
    (print-matrix (** 10 matrix1))
    ; operace provedené mezi dvěma maticemi
    (print-matrix (** matrix1 matrix2))
    ; operátor ekvivalence aplikovaný na dvojici matic
    (print-vector (== matrix1 matrix2))
    (print-vector (== matrix1 matrix1))
    (print-vector (== matrix1 (identity-matrix 3 3)))
    (print-vector (== matrix2 (array [[1 2 3] [4 5 6] [7 8 9]])))
    (println))

Opět se podívejme na výsledky:

*** Test speciálních operátorů aplikovaných na matice ***
Vstupní data (matice):
    matrix1 =
[[1.000 0.000 0.000]
 [0.000 1.000 0.000]
 [0.000 0.000 1.000]]
 
    matrix2 =
[[1.000 2.000 3.000]
 [4.000 5.000 6.000]
 [7.000 8.000 9.000]]
 
Vyhodnocení:
    (** matrix1 10) =
[[1.000 0.000 0.000]
 [0.000 1.000 0.000]
 [0.000 0.000 1.000]]
 
    (** 10 matrix1) =
[[10.000  1.000  1.000]
 [ 1.000 10.000  1.000]
 [ 1.000  1.000 10.000]]
 
    (** matrix1 matrix2) =
[[1.000 0.000 0.000]
 [0.000 1.000 0.000]
 [0.000 0.000 1.000]]
 
    (== matrix1 matrix2)             =  false
    (== matrix1 matrix1)             =  true
    (== matrix1 (identity-matrix 3 3)) =  true
    (== matrix2 (array [[1 2 3] [4 5 6] [7 8 9]])) =  true

Podívejme se ještě na některé významné funkce, které je možné aplikovat na matice. Některé z těchto funkcí vrací skalární hodnotu, jiné naopak vektor či (jinou) matici. Některé z těchto funkcí již známe z kapitol o vektorech, další funkce jsou zcela nové:

# Funkce Výsledek Popis
1 emin skalár nalezne nejmenší prvek matice
2 emax skalár nalezne největší prvek matice
3 esum skalár sečte prvky v matici
4 ecount skalár vrátí celkový počet prvků matice
5 zero-count skalár vrátí počet nulových prvků matice
6 inverse matice výpočet inverzní matice
7 non-zero-indices matice vrací indexy nenulových prvků pro všechny řezy maticí
8 outer-product matice (vyšší počet dimenzí) vnější součin matice/matic či vektorů
9 join matice spojení dvou či více matic (zvětší se počet řádků)

Nezbývá než si chování výše popsaných funkcí vyzkoušet:

(defn immutable-matrix-functions
    "Test chování vybraných funkcí, které lze nalézt ve jmenných prostorech
     clojure.core.matrix a clojure.core.matrix.operators."
    [matrix1 matrix2 matrix3]
    (println "*** Test funkcí aplikovaných na matice ***")
    (println "Vstupní data (matice):")
    (print-matrix matrix1)
    (print-matrix matrix2)
    (print-matrix matrix3)
    (println "Vyhodnocení:")
    (print-vector (emin matrix1))
    (print-vector (emin matrix2))
    (print-vector (emin matrix3))
    (print-vector (emax matrix1))
    (print-vector (emax matrix2))
    (print-vector (emax matrix3))
    (print-vector (esum matrix1))
    (print-vector (esum matrix2))
    (print-vector (esum matrix3))
    (print-vector (ecount matrix1))
    (print-vector (ecount matrix2))
    (print-vector (ecount matrix3))
    (print-vector (zero-count matrix1))
    (print-vector (zero-count matrix2))
    (print-vector (zero-count matrix3))
    (print-matrix (inverse matrix1))
    (print-matrix (inverse matrix2))
    (print-matrix (inverse matrix3))
    (print-matrix (non-zero-indices matrix1))
    (print-matrix (non-zero-indices matrix2))
    (print-matrix (non-zero-indices matrix3))
    (print-matrix (outer-product matrix1))
    (print-matrix (outer-product matrix2))
    (print-matrix (outer-product matrix3))
    (print-matrix (join matrix1 matrix2))
    (println))

Po zavolání této funkce by se na standardní výstup měly vypsat následující řádky:

*** Test funkcí aplikovaných na matice ***
Vstupní data (matice):
    matrix1 =
[[1.000 0.000 0.000]
 [0.000 1.000 0.000]
 [0.000 0.000 1.000]]
 
    matrix2 =
[[1.000 2.000 3.000]
 [4.000 5.000 6.000]
 [7.000 8.000 9.000]]
 
    matrix3 =
[[0.000 1.000]
 [2.000 3.000]]
 
Vyhodnocení:
    (emin matrix1)                   =  0.000
    (emin matrix2)                   =  1.000
    (emin matrix3)                   =  0.000
    (emax matrix1)                   =  1.000
    (emax matrix2)                   =  9.000
    (emax matrix3)                   =  3.000
    (esum matrix1)                   =  3.000
    (esum matrix2)                   =  45.000
    (esum matrix3)                   =  6.000
    (ecount matrix1)                 =  9.000
    (ecount matrix2)                 =  9.000
    (ecount matrix3)                 =  4.000
    (zero-count matrix1)             =  6.000
    (zero-count matrix2)             =  0.000
    (zero-count matrix3)             =  1.000
 
    (inverse matrix1) =
[[1.000 0.000 0.000]
 [0.000 1.000 0.000]
 [0.000 0.000 1.000]]
 
    (inverse matrix2) =
[[-4503599627370498.000   9007199254740992.000 -4503599627370496.000]
 [ 9007199254740996.000 -18014398509481984.000  9007199254740991.000]
 [-4503599627370498.000   9007199254740992.000 -4503599627370495.500]]
 
    (inverse matrix3) =
[[-1.500 0.500]
 [ 1.000 0.000]]
 
    (non-zero-indices matrix1) =
[[0.000]
 [1.000]
 [2.000]]
 
    (non-zero-indices matrix2) =
[[0.000 1.000 2.000]
 [0.000 1.000 2.000]
 [0.000 1.000 2.000]]
 
    (non-zero-indices matrix3) =
[[1.000]
 [0.000]]
 
    (outer-product matrix1) =
[[1.000 0.000 0.000]
 [0.000 1.000 0.000]
 [0.000 0.000 1.000]]
 
    (outer-product matrix2) =
[[1.000 2.000 3.000]
 [4.000 5.000 6.000]
 [7.000 8.000 9.000]]
 
    (outer-product matrix3) =
[[0.000 1.000]
 [2.000 3.000]]
 
    (join matrix1 matrix2) =
[[1.000 0.000 0.000]
 [0.000 1.000 0.000]
 [0.000 0.000 1.000]
 [1.000 2.000 3.000]
 [4.000 5.000 6.000]
 [7.000 8.000 9.000]]

Vnější součin dvou matic již dá složitější výsledek (jedná se vlastně o výsledek součinu každého prvku s každým prvkem a uspořádání do matice se čtyřmi rozměry):

(pm (outer-product m2 m2))
[[[[ 1.000  2.000  3.000]
   [ 4.000  5.000  6.000]
   [ 7.000  8.000  9.000]]
  [[ 2.000  4.000  6.000]
   [ 8.000 10.000 12.000]
   [14.000 16.000 18.000]]
  [[ 3.000  6.000  9.000]
   [12.000 15.000 18.000]
   [21.000 24.000 27.000]]]
 [[[ 4.000  8.000 12.000]
   [16.000 20.000 24.000]
   [28.000 32.000 36.000]]
  [[ 5.000 10.000 15.000]
   [20.000 25.000 30.000]
   [35.000 40.000 45.000]]
  [[ 6.000 12.000 18.000]
   [24.000 30.000 36.000]
   [42.000 48.000 54.000]]]
 [[[ 7.000 14.000 21.000]
   [28.000 35.000 42.000]
   [49.000 56.000 63.000]]
  [[ 8.000 16.000 24.000]
   [32.000 40.000 48.000]
   [56.000 64.000 72.000]]
  [[ 9.000 18.000 27.000]
   [36.000 45.000 54.000]
   [63.000 72.000 81.000]]]]

Jednodušší je vysvětlení chování této funkce na dvojici vektorů – jednorozměrných matic:

(def v1 (array [1 2 3]))
 
(pm (outer-product v1 v1))
[[1.000 2.000 3.000]
 [2.000 4.000 6.000]
 [3.000 6.000 9.000]]

Příklad z praxe – tabulka malé násobilky:

(def v1 (array (range 1 11)))
 
(pm (outer-product v1 v1))
[[ 1.000  2.000  3.000  4.000  5.000  6.000  7.000  8.000  9.000  10.000]
 [ 2.000  4.000  6.000  8.000 10.000 12.000 14.000 16.000 18.000  20.000]
 [ 3.000  6.000  9.000 12.000 15.000 18.000 21.000 24.000 27.000  30.000]
 [ 4.000  8.000 12.000 16.000 20.000 24.000 28.000 32.000 36.000  40.000]
 [ 5.000 10.000 15.000 20.000 25.000 30.000 35.000 40.000 45.000  50.000]
 [ 6.000 12.000 18.000 24.000 30.000 36.000 42.000 48.000 54.000  60.000]
 [ 7.000 14.000 21.000 28.000 35.000 42.000 49.000 56.000 63.000  70.000]
 [ 8.000 16.000 24.000 32.000 40.000 48.000 56.000 64.000 72.000  80.000]
 [ 9.000 18.000 27.000 36.000 45.000 54.000 63.000 72.000 81.000  90.000]
 [10.000 20.000 30.000 40.000 50.000 60.000 70.000 80.000 90.000 100.000]]

10. Měnitelné matice

Podobně jako bylo možné se při vytváření vektorů rozhodnout, zda se má jednat o neměnitelné či naopak o modifikovatelné datové struktury, lze totéž rozhodnutí provést i v případě matix zavoláním funkce mutable. Použití modifikovatelných matic může v mnoha případech znamenat znatelné urychlení mnoha operací a taktéž menší nároky na kapacitu operační paměti. Záleží tedy jen na vývojáři, ve kterých případech se přikloní k použití neměnitelných matic a tím pádem i k funkcionálnímu stylu programování a kdy naopak kvůli paměťovým a časovým nárokům upřednostní matice, jejichž prvky je možné kdykoli změnit (a to i z jiných vláken). Podívejme se nyní na několik příkladů použití měnitelných matic:

(defn mutable-matrix-tests
    []
    (let [m0 (mutable (zero-matrix 3 3))
          m1 (mutable (identity-matrix 3 3))
          m2 (mutable (array [[1 2 3] [4 5 6] [7 8 9]]))]
          (mutable-matrix-operators m1 m2)
          (mutable-matrix-functions m0 m1 m2)))

V případě použití měnitelných matic lze použít funkce/operátory +=, -=, *= a div=, což je ukázáno ve funkci nazvané mutable-matrix-operators:

(defn mutable-matrix-operators
    "Test chování operátorů předefinovaných ve jmenném prostoru
     clojure.core.matrix.operators na měnitelné matice"
    [matrix1 matrix2]
    (println "*** Test základních operátorů aplikovaných na měnitelné matice ***")
    (println "Vstupní data (matice):")
    (print-matrix matrix1)
    (print-matrix matrix2)
    (println "Vyhodnocení:")
    ; operace provedené mezi dvěma maticemi
    (print-matrix (+= matrix1 matrix2))
    (print-matrix (-= matrix1 matrix2))
    (print-matrix (*= matrix1 matrix2))
    (print-matrix (div= matrix1 matrix2))
    (println))

Po zavolání této funkce se na standardní výstup vypíšou tyto řádky:

*** Test základních operátorů aplikovaných na měnitelné matice ***
Vstupní data (matice):
    matrix1 =
[[1.000 0.000 0.000]
 [0.000 1.000 0.000]
 [0.000 0.000 1.000]]
 
    matrix2 =
[[1.000 2.000 3.000]
 [4.000 5.000 6.000]
 [7.000 8.000 9.000]]
 
Vyhodnocení:
    (+= matrix1 matrix2) =
[[2.000 2.000  3.000]
 [4.000 6.000  6.000]
 [7.000 8.000 10.000]]
 
    (-= matrix1 matrix2) =
[[1.000 0.000 0.000]
 [0.000 1.000 0.000]
 [0.000 0.000 1.000]]
 
    (*= matrix1 matrix2) =
[[1.000 0.000 0.000]
 [0.000 5.000 0.000]
 [0.000 0.000 9.000]]
 
    (div= matrix1 matrix2) =
[[1.000 0.000 0.000]
 [0.000 1.000 0.000]
 [0.000 0.000 1.000]]

Všechny funkce, které mění prvky matice, mají na konci svého jména znak !:

(defn mutable-matrix-functions
    "test chování vybraných funkcí, které lze nalézt ve jmenných prostorech
     clojure.core.matrix a clojure.core.matrix.operators na měnitelné matice."
    [matrix1 matrix2 matrix3]
    (println "*** test funkcí aplikovaných na měnitelné matice ***")
    (println "vstupní data (matice):")
    (print-matrix matrix1)
    (print-matrix matrix2)
    (print-matrix matrix3)
    (println "vyhodnocení:")
    (print-matrix (sin! matrix1))
    (print-matrix (sin! matrix2))
    (print-matrix (sin! matrix3))
    (print-matrix (fill! matrix1 0))
    (print-matrix (fill! matrix2 1))
    (print-matrix (fill! matrix3 2))
    (println))

Po zavolání výše vypsané funkce mutable-matrix-functions se na standardní výstup vypíšou následující řádky:

*** Test funkcí aplikovaných na měnitelné matice ***
Vstupní data (matice):
    matrix1 =
[[0.000 0.000 0.000]
 [0.000 0.000 0.000]
 [0.000 0.000 0.000]]
 
    matrix2 =
[[1.000 0.000 0.000]
 [0.000 1.000 0.000]
 [0.000 0.000 1.000]]
 
    matrix3 =
[[1.000 2.000 3.000]
 [4.000 5.000 6.000]
 [7.000 8.000 9.000]]
 
Vyhodnocení:
    (sin! matrix1) =
[[0.000 0.000 0.000]
 [0.000 0.000 0.000]
 [0.000 0.000 0.000]]
 
    (sin! matrix2) =
[[0.841 0.000 0.000]
 [0.000 0.841 0.000]
 [0.000 0.000 0.841]]
 
    (sin! matrix3) =
[[ 0.841  0.909  0.141]
 [-0.757 -0.959 -0.279]
 [ 0.657  0.989  0.412]]
 
    (fill! matrix1 0) =
[[0.000 0.000 0.000]
 [0.000 0.000 0.000]
 [0.000 0.000 0.000]]
 
    (fill! matrix2 1) =
[[1.000 1.000 1.000]
 [1.000 1.000 1.000]
 [1.000 1.000 1.000]]
 
    (fill! matrix3 2) =
[[2.000 2.000 2.000]
 [2.000 2.000 2.000]
 [2.000 2.000 2.000]]

11. Řídké matice, interní reprezentace matic

V knihovně core.matrix je deklarováno i několik funkcí sloužících pro vytvoření takzvaných řídkých matic neboli sparse matrices. Tyto matice jsou charakteristické tím, že mnoho jejich prvků je nulových, takže většinou nemá význam matici reprezentovat běžným dvourozměrným polem, ale je možné využít vylepšené způsoby uložení a taktéž optimalizované algoritmy výpočtu některých operací, například pro násobení matic (musíme si uvědomit, že mnoho řídkých matic má opravdu velké rozměry, příkladem může být tzv. Google matice). Samotná knihovna core.matrix přesně nespecifikuje, jakým způsobem mají být normální matice i řídké matice uloženy v operační paměti, protože je možné použít různé implementace rozhraní deklarovaného v této knihovně. V základním nastavení lze použít následující implementace:

Implementace Typ Popis
persistent-vector immutable implementace pomocí vektorů jazyka Clojure
double-array mutable implementace pomocí javovských polí
sequene immutable používáno především při načítání dat z jiných částí aplikace
ndarray m/i používá strukturu NDArray navrženou Dmitrijem Groševem
ndarray-double m/i přesná specifikace typů prvků
ndarray-long m/i přesná specifikace typů prvků (lze i další typy)

Datová struktura NDArray interně využívá jednorozměrná javovská pole a navíc si pamatuje i počet prvků v jednotlivých dimenzích, tj. vlastně ofsety mezi řádky matice (popř. mezi jednotlivými 2D maticemi u matic s větším počtem dimenzí).

12. Pohledy (views)

Poslední vlastností matic, s níž se v dnešním článku seznámíme, je podpora takzvaných pohledů neboli views. Při implementaci některých algoritmů je totiž nutné použít z větší matice pouze její část, ovšem mnohdy je kvůli časovým i paměťovým nárokům neefektivní tuto část kopírovat (klonovat). Jednodušší a rychlejší je použít právě pohledy, s jejichž využitím je možné (z pohledu programátora) vytvořit kopii části matice, ve skutečnosti se však bude skutečně jednat o jakýsi „pohled“ do obecně větší matice původní. Pohledy v core.matrix se tedy skutečně podobají pohledům známým například z relačních databází. Pro vysvětlení způsobu vytváření pohledů se opět podívejme na demonstrační příklad:

(defn views-tests
    "Pohledy na matice."
    []
    (println "*** Test pohledů na matice ***")
    (let [m1 big-matrix]
        (println "Vstupní matice:")
        (print-matrix m1)
        (as-vector-test m1)
        (submatrix-test m1)
        (select-test m1)))

První funkcí, která dokáže vytvořit pohled, je funkce nazvaná as-vector, kterou lze použít pro (zdánlivý) převod matice na vektor. Pokud je zapotřebí provést skutečný převod, lze použít výše popsanou funkci to-vector, o níž jsme se zmínili v první kapitole:

(defn as-vector-test
    "Test funkce as-vector."
    [matrix]
    (print-vector (as-vector matrix)))

Pravděpodobně užitečnější funkcí pro tvorbu pohledu je funkce nazvaná submatrix, jejíž význam je zřejmý již z jejího pojmenování. Zajímavé je, že tuto funkci lze volat s různým typem a počtem parametrů: buď se zadávájí první a poslední indexy prvků v každé dimenzi, nebo se použije vektor obsahující rozměry a začátek výsledné podmatice:

(defn submatrix-test
    "Test funkce submatrix."
    [matrix]
    (print-matrix (submatrix matrix 0 3 0 3))
    (print-matrix (submatrix matrix 1 5 2 4))
    (print-matrix (submatrix matrix 0 5 0 1))
    (print-matrix (submatrix matrix 0 1 0 5))
    (print-matrix (submatrix matrix [[0 10][2 2]])))

Nejsložitější je funkce nazvaná select, která taktéž vrací pohled. Tuto funkci lze využít pro výběr celých řádků či sloupců, a to s využitím speciální hodnoty :all, která se do selectu předá namísto indexu. Kromě toho lze zadat i přesný index prvku či prvků vybíraných z matice (počet indexů odpovídá počtu dimenzí matice):

(defn select-test
    "Test funkce select."
    [matrix]
    (print-matrix (select matrix 0 0))
    (print-matrix (select matrix 5 5))
    (print-matrix (select matrix 1 :all))
    (print-matrix (select matrix :all 1))
    (print-matrix (select matrix 9 :all))
    (print-matrix (select matrix :all 9)))

Výsledky běhu všech tří testovacích funkcí jsou zobrazeny pod tímto odstavcem:

*** Test pohledů na matice ***
Vstupní matice:
    m1 =
[[ 0.000  1.000  2.000  3.000  4.000  5.000  6.000  7.000  8.000  9.000]
 [10.000 11.000 12.000 13.000 14.000 15.000 16.000 17.000 18.000 19.000]
 [20.000 21.000 22.000 23.000 24.000 25.000 26.000 27.000 28.000 29.000]
 [30.000 31.000 32.000 33.000 34.000 35.000 36.000 37.000 38.000 39.000]
 [40.000 41.000 42.000 43.000 44.000 45.000 46.000 47.000 48.000 49.000]
 [50.000 51.000 52.000 53.000 54.000 55.000 56.000 57.000 58.000 59.000]
 [60.000 61.000 62.000 63.000 64.000 65.000 66.000 67.000 68.000 69.000]
 [70.000 71.000 72.000 73.000 74.000 75.000 76.000 77.000 78.000 79.000]
 [80.000 81.000 82.000 83.000 84.000 85.000 86.000 87.000 88.000 89.000]
 [90.000 91.000 92.000 93.000 94.000 95.000 96.000 97.000 98.000 99.000]]
 
    (as-vector matrix)               =  [0.000 1.000 2.000 3.000 4.000 5.000 6.000 7.000 8.000 9.000 10.000 11.000 12.000 13.000 14.000 15.000 16.000 17.000 18.000 19.000 20.000 21.000 22.000 23.000 24.000 25.000 26.000 27.000 28.000 29.000 30.000 31.000 32.000 33.000 34.000 35.000 36.000 37.000 38.000 39.000 40.000 41.000 42.000 43.000 44.000 45.000 46.000 47.000 48.000 49.000 50.000 51.000 52.000 53.000 54.000 55.000 56.000 57.000 58.000 59.000 60.000 61.000 62.000 63.000 64.000 65.000 66.000 67.000 68.000 69.000 70.000 71.000 72.000 73.000 74.000 75.000 76.000 77.000 78.000 79.000 80.000 81.000 82.000 83.000 84.000 85.000 86.000 87.000 88.000 89.000 90.000 91.000 92.000 93.000 94.000 95.000 96.000 97.000 98.000 99.000]
 
    (submatrix matrix 0 3 0 3) =
[[ 0.000  1.000  2.000]
 [10.000 11.000 12.000]
 [20.000 21.000 22.000]]
 
    (submatrix matrix 1 5 2 4) =
[[12.000 13.000 14.000 15.000]
 [22.000 23.000 24.000 25.000]
 [32.000 33.000 34.000 35.000]
 [42.000 43.000 44.000 45.000]
 [52.000 53.000 54.000 55.000]]
 
    (submatrix matrix 0 5 0 1) =
[[ 0.000]
 [10.000]
 [20.000]
 [30.000]
 [40.000]]
 
    (submatrix matrix 0 1 0 5) =
[[0.000 1.000 2.000 3.000 4.000]]
 
    (submatrix matrix [[0 10] [2 2]]) =
[[ 2.000  3.000]
 [12.000 13.000]
 [22.000 23.000]
 [32.000 33.000]
 [42.000 43.000]
 [52.000 53.000]
 [62.000 63.000]
 [72.000 73.000]
 [82.000 83.000]
 [92.000 93.000]]
 
    (select matrix 0 0) =
0.000
 
    (select matrix 5 5) =
55.000
 
    (select matrix 1 :all) =
[10.000 11.000 12.000 13.000 14.000 15.000 16.000 17.000 18.000 19.000]
 
    (select matrix :all 1) =
[1.000 11.000 21.000 31.000 41.000 51.000 61.000 71.000 81.000 91.000]
 
    (select matrix 9 :all) =
[90.000 91.000 92.000 93.000 94.000 95.000 96.000 97.000 98.000 99.000]
 
    (select matrix :all 9) =
[9.000 19.000 29.000 39.000 49.000 59.000 69.000 79.000 89.000 99.000]

13. Úplný zdrojový kód druhého demonstračního příkladu

V této kapitole bude uveden výpis úplného zdrojového kódu dnešního druhého demonstračního příkladu, z něhož jsme používali úryvky v předchozích čtyřech kapitolách:

project.clj:

widgety

(defproject matrix2 "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.6.0"]
                 [net.mikera/core.matrix "0.34.0"]]
  :main ^:skip-aot matrix2.core
  :target-path "target/%s"
  :profiles {:uberjar {:aot :all}})

core.clj:

(ns matrix2.core
  (:gen-class))
 
(use 'clojure.core.matrix)
(use 'clojure.core.matrix.operators)
 
(defmacro print-vector
    "Makro, které na standardní výstup vypíše výraz a následně i
     hodnotu tohoto výrazu s použitím funkce pm (pretty print matrix).
     Předpokládá se, že se výraz vyhodnotí na vektor."
    [expression]
    `(do (printf "    %-32s =  " '~expression)
         (pm ~expression)))
 
(defmacro print-matrix
    "Makro, které na standardní výstup vypíše výraz a následně i
     hodnotu tohoto výrazu s použitím funkce pm (pretty print matrix).
     Předpokládá se, že se výraz vyhodnotí na matici"
    [expression]
    `(do (println "   " '~expression "=")
         (pm ~expression)
         (println)))
 
(defn immutable-matrix-operators
    "Test chování operátorů předefinovaných ve jmenném prostoru
     clojure.core.matrix.operators."
    [matrix1 matrix2]
    (println "*** Test základních operátorů aplikovaných na matice ***")
    (println "Vstupní data (matice):")
    (print-matrix matrix1)
    (print-matrix matrix2)
    (println "Vyhodnocení:")
    ; operace provedené mezi maticí a skalární hodnotou
    (print-matrix (+ matrix1 10))
    (print-matrix (- matrix1 10))
    (print-matrix (* matrix1 10))
    (print-matrix (/ matrix1 10))
    ; operace provedené mezi skalární hodnotou a maticí
    (print-matrix (+ 10 matrix1))
    (print-matrix (- 10 matrix1))
    (print-matrix (* 10 matrix1))
    (try
        (print-matrix (/ 10 matrix1))
        (catch java.lang.ArithmeticException e
            (println "Exception: " (.toString e))))
    ; operace provedené mezi dvěma maticemi
    (print-matrix (+ matrix1 matrix2))
    (print-matrix (- matrix1 matrix2))
    (print-matrix (* matrix1 matrix2))
    (try
        (print-matrix (/ matrix1 matrix2))
        (catch java.lang.ArithmeticException e
            (println "Exception: " (.toString e))))
    (println))
 
(defn immutable-matrix-special-operators
    "Test chování speciálních operátorů předefinovaných
     ve jmenném prostoru clojure.core.matrix.operators."
    [matrix1 matrix2]
    (println "*** Test speciálních operátorů aplikovaných na matice ***")
    (println "Vstupní data (matice):")
    (print-matrix matrix1)
    (print-matrix matrix2)
    (println "Vyhodnocení:")
    ; operace provedené mezi maticí a skalární hodnotou
    (print-matrix (** matrix1 10))
    ; operace provedené mezi skalární hodnotou a maticí
    (print-matrix (** 10 matrix1))
    ; operace provedené mezi dvěma maticemi
    (print-matrix (** matrix1 matrix2))
    ; operátor ekvivalence aplikovaný na dvojici matic
    (print-vector (== matrix1 matrix2))
    (print-vector (== matrix1 matrix1))
    (print-vector (== matrix1 (identity-matrix 3 3)))
    (print-vector (== matrix2 (array [[1 2 3] [4 5 6] [7 8 9]])))
    (println))
 
(defn immutable-matrix-functions
    "Test chování vybraných funkcí, které lze nalézt ve jmenných prostorech
     clojure.core.matrix a clojure.core.matrix.operators."
    [matrix1 matrix2 matrix3]
    (println "*** Test funkcí aplikovaných na matice ***")
    (println "Vstupní data (matice):")
    (print-matrix matrix1)
    (print-matrix matrix2)
    (print-matrix matrix3)
    (println "Vyhodnocení:")
    (print-vector (emin matrix1))
    (print-vector (emin matrix2))
    (print-vector (emin matrix3))
    (print-vector (emax matrix1))
    (print-vector (emax matrix2))
    (print-vector (emax matrix3))
    (print-vector (esum matrix1))
    (print-vector (esum matrix2))
    (print-vector (esum matrix3))
    (print-vector (ecount matrix1))
    (print-vector (ecount matrix2))
    (print-vector (ecount matrix3))
    (print-vector (zero-count matrix1))
    (print-vector (zero-count matrix2))
    (print-vector (zero-count matrix3))
    (print-matrix (inverse matrix1))
    (print-matrix (inverse matrix2))
    (print-matrix (inverse matrix3))
    (print-matrix (non-zero-indices matrix1))
    (print-matrix (non-zero-indices matrix2))
    (print-matrix (non-zero-indices matrix3))
    (print-matrix (outer-product matrix1))
    (print-matrix (outer-product matrix2))
    (print-matrix (outer-product matrix3))
    (print-matrix (join matrix1 matrix2))
    (println))
 
(defn immutable-matrix-tests
    "Spustí všechny testovací funkce s neměnitelnými maticemi"
    []
    (let [m0 (zero-matrix 3 3)
          m1 (identity-matrix 3 3)
          m2 (array [[1 2 3] [4 5 6] [7 8 9]])
          m3 (array [[0 1] [2 3]])]
          (immutable-matrix-operators m0 m1)
          (immutable-matrix-operators m1 m2)
          (immutable-matrix-special-operators m1 m2)
          (immutable-matrix-functions m1 m2 m3)))
 
(defn mutable-matrix-operators
    "Test chování operátorů předefinovaných ve jmenném prostoru
     clojure.core.matrix.operators na měnitelné matice"
    [matrix1 matrix2]
    (println "*** Test základních operátorů aplikovaných na měnitelné matice ***")
    (println "Vstupní data (matice):")
    (print-matrix matrix1)
    (print-matrix matrix2)
    (println "Vyhodnocení:")
    ; operace provedené mezi dvěma maticemi
    (print-matrix (+= matrix1 matrix2))
    (print-matrix (-= matrix1 matrix2))
    (print-matrix (*= matrix1 matrix2))
    (print-matrix (div= matrix1 matrix2))
    (println))
 
(defn mutable-matrix-functions
    "test chování vybraných funkcí, které lze nalézt ve jmenných prostorech
     clojure.core.matrix a clojure.core.matrix.operators na měnitelné matice."
    [matrix1 matrix2 matrix3]
    (println "*** test funkcí aplikovaných na měnitelné matice ***")
    (println "vstupní data (matice):")
    (print-matrix matrix1)
    (print-matrix matrix2)
    (print-matrix matrix3)
    (println "vyhodnocení:")
    (print-matrix (sin! matrix1))
    (print-matrix (sin! matrix2))
    (print-matrix (sin! matrix3))
    (print-matrix (fill! matrix1 0))
    (print-matrix (fill! matrix2 1))
    (print-matrix (fill! matrix3 2))
    (println))
 
(defn mutable-matrix-tests
    []
    (let [m0 (mutable (zero-matrix 3 3))
          m1 (mutable (identity-matrix 3 3))
          m2 (mutable (array [[1 2 3] [4 5 6] [7 8 9]]))]
          (mutable-matrix-operators m1 m2)
          (mutable-matrix-functions m0 m1 m2)))
 
; vytvoření matice o rozměrech 10x10 prvků
(def big-matrix
    (-> (range 0 100) (reshape [10 10])))
 
(defn as-vector-test
    "Test funkce as-vector."
    [matrix]
    (print-vector (as-vector matrix)))
 
(defn submatrix-test
    "Test funkce submatrix."
    [matrix]
    (print-matrix (submatrix matrix 0 3 0 3))
    (print-matrix (submatrix matrix 1 5 2 4))
    (print-matrix (submatrix matrix 0 5 0 1))
    (print-matrix (submatrix matrix 0 1 0 5))
    (print-matrix (submatrix matrix [[0 10][2 2]])))
 
(defn select-test
    "Test funkce select."
    [matrix]
    (print-matrix (select matrix 0 0))
    (print-matrix (select matrix 5 5))
    (print-matrix (select matrix 1 :all))
    (print-matrix (select matrix :all 1))
    (print-matrix (select matrix 9 :all))
    (print-matrix (select matrix :all 9)))
 
(defn views-tests
    "Pohledy na matice."
    []
    (println "*** Test pohledů na matice ***")
    (let [m1 big-matrix]
        (println "Vstupní matice:")
        (print-matrix m1)
        (as-vector-test m1)
        (submatrix-test m1)
        (select-test m1)))
 
(defn -main
    "Vítejte v Matrixu potřetí..."
    [& args]
    (immutable-matrix-tests)
    (mutable-matrix-tests)
    (views-tests))
 

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

Oba dva dnes popsané demonstrační příklady byly, podobně jako v předchozích částech tohoto seriálu, uloženy do GIT repositáře dostupného na adrese https://github.com/tisnik/clojure-examples. V tabulce zobrazené pod tímto odstavcem naleznete na jednotlivé příklady přímé odkazy:

15. Odkazy na předchozí části seriálu

  1. Leiningen: nástroj pro správu projektů napsaných v Clojure
    http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure/
  2. Leiningen: nástroj pro správu projektů napsaných v Clojure (2)
    http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-2/
  3. Leiningen: nástroj pro správu projektů napsaných v Clojure (3)
    http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-3/
  4. Leiningen: nástroj pro správu projektů napsaných v Clojure (4)
    http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-4/
  5. Leiningen: nástroj pro správu projektů napsaných v Clojure (5)
    http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-5/
  6. Leiningen: nástroj pro správu projektů napsaných v Clojure (6)
    http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-6/
  7. Programovací jazyk Clojure a databáze (1.část)
    http://www.root.cz/clanky/programovaci-jazyk-clojure-a-databaze-1-cast/
  8. Pluginy pro Leiningen
    http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-pluginy-pro-leiningen/
  9. Programovací jazyk Clojure a knihovny pro práci s vektory a maticemi
    http://www.root.cz/clanky/programovaci-jazyk-clojure-a-knihovny-pro-praci-s-vektory-a-maticemi/

16. Odkazy na Internetu

  1. Dokumentace vygenerovaná pro knihovnu core.matrix
    https://cloojure.github.i­o/doc/core.matrix/index.html
  2. Size and Dimensionality
    https://groups.google.com/fo­rum/#!topic/numerical-clojure/zebBCa68eTw/discussion
  3. Towards core.matrix for Clojure?
    https://clojurefun.wordpres­s.com/2013/01/05/towards-core-matrix-for-clojure/
  4. The Clojure Toolbox
    http://www.clojure-toolbox.com/
  5. Neanderthal
    http://neanderthal.uncomplicate.org/
  6. Hello world project
    https://github.com/uncompli­cate/neanderthal/blob/mas­ter/examples/hello-world/project.clj
  7. vectorz-clj
    https://github.com/mikera/vectorz-clj
  8. vectorz – Examples
    https://github.com/mikera/vectorz-clj/wiki/Examples
  9. gloss
    https://github.com/ztellman/gloss
  10. HTTP client/server for Clojure
    http://www.http-kit.org/
  11. Array Programming
    https://en.wikipedia.org/wi­ki/Array_programming
  12. Discovering Array Languages
    http://archive.vector.org­.uk/art10008110
  13. no stinking loops – Kalothi
    http://www.nsl.com/
  14. Vector (obsahuje odkazy na články, knihy a blogy o programovacích jazycích APL, J a K)
    http://www.vector.org.uk/
  15. APL Interpreters
    http://www.vector.org.uk/?a­rea=interpreters
  16. APL_(programming_language
    http://en.wikipedia.org/wi­ki/APL_(programming_langu­age
  17. APL FAQ
    http://www.faqs.org/faqs/apl-faq/
  18. APL FAQ (nejnovější verze)
    http://home.earthlink.net/~swsir­lin/apl.faq.html
  19. A+
    http://www.aplusdev.org/
  20. APLX
    http://www.microapl.co.uk/
  21. FreeAPL
    http://www.pyr.fi/apl/index.htm
  22. J: a modern, high-level, general-purpose, high-performance programming language
    http://www.jsoftware.com/
  23. K, Kdb: an APL derivative for Solaris, Linux, Windows
    http://www.kx.com
  24. openAPL (GPL)
    http://sourceforge.net/pro­jects/openapl
  25. Parrot APL (GPL)
    http://www.parrotcode.org/
  26. Learning J (Roger Stokes)
    http://www.jsoftware.com/hel­p/learning/contents.htm
  27. Rosetta Code
    http://rosettacode.org/wiki/Main_Page
  28. Why APL
    http://www.acm.org/sigapl/whyapl.htm
  29. java.jdbc API Reference
    https://clojure.github.io/java.jdbc/
  30. Hiccup
    https://github.com/weavejester/hiccup
  31. Clojure Ring na GitHubu
    https://github.com/ring-clojure/ring
  32. A brief overview of the Clojure web stack
    https://brehaut.net/blog/2011/rin­g_introduction
  33. Getting Started with Ring
    http://www.learningclojure­.com/2013/01/getting-started-with-ring.html
  34. Getting Started with Ring and Compojure – Clojure Web Programming
    http://www.myclojureadven­ture.com/2011/03/getting-started-with-ring-and-compojure.html
  35. Unit Testing in Clojure
    http://nakkaya.com/2009/11/18/unit-testing-in-clojure/
  36. Testing in Clojure (Part-1: Unit testing)
    http://blog.knoldus.com/2014/03/22/tes­ting-in-clojure-part-1-unit-testing/
  37. API for clojure.test – Clojure v1.6 (stable)
    https://clojure.github.io/clo­jure/clojure.test-api.html
  38. Leiningen: úvodní stránka
    http://leiningen.org/
  39. Leiningen: Git repository
    https://github.com/techno­mancy/leiningen
  40. leiningen-win-installer
    http://leiningen-win-installer.djpowell.net/
  41. Clojure 1: Úvod
    http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm/
  42. Clojure 2: Symboly, kolekce atd.
    http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm-2-cast/
  43. Clojure 3: Funkcionální programování
    http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm-3-cast-funkcionalni-programovani/
  44. Clojure 4: Kolekce, sekvence a lazy sekvence
    http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm-4-cast-kolekce-sekvence-a-lazy-sekvence/
  45. Clojure 5: Sekvence, lazy sekvence a paralelní programy
    http://www.root.cz/clanky/clojure-a-bezpecne-aplikace-pro-jvm-sekvence-lazy-sekvence-a-paralelni-programy/
  46. Clojure 6: Podpora pro paralelní programování
    http://www.root.cz/clanky/programovaci-jazyk-clojure-6-futures-nejsou-jen-financni-derivaty/
  47. Clojure 7: Další funkce pro paralelní programování
    http://www.root.cz/clanky/programovaci-jazyk-clojure-7-dalsi-podpurne-prostredky-pro-paralelni-programovani/
  48. Clojure 8: Identity, stavy, neměnné hodnoty a reference
    http://www.root.cz/clanky/programovaci-jazyk-clojure-8-identity-stavy-nemenne-hodnoty-a-referencni-typy/
  49. Clojure 9: Validátory, pozorovatelé a kooperace s Javou
    http://www.root.cz/clanky/programovaci-jazyk-clojure-9-validatory-pozorovatele-a-kooperace-mezi-clojure-a-javou/
  50. Clojure 10: Kooperace mezi Clojure a Javou
    http://www.root.cz/clanky/programovaci-jazyk-clojure-10-kooperace-mezi-clojure-a-javou-pokracovani/
  51. Clojure 11: Generátorová notace seznamu/list comprehension
    http://www.root.cz/clanky/programovaci-jazyk-clojure-11-generatorova-notace-seznamu-list-comprehension/
  52. Clojure 12: Překlad programů z Clojure do bajtkódu JVM I
    http://www.root.cz/clanky/programovaci-jazyk-clojure-12-preklad-programu-z-clojure-do-bajtkodu-jvm/
  53. Clojure 13: Překlad programů z Clojure do bajtkódu JVM II
    2) http://www.root.cz/clanky/programovaci-jazyk-clojure-13-preklad-programu-z-clojure-do-bajtkodu-jvm-pokracovani/
  54. Clojure 14: Základy práce se systémem maker
    http://www.root.cz/clanky/programovaci-jazyk-clojure-14-zaklady-prace-se-systemem-maker/
  55. Clojure 15: Tvorba uživatelských maker
    http://www.root.cz/clanky/programovaci-jazyk-clojure-15-tvorba-uzivatelskych-maker/
  56. Clojure 16: Složitější uživatelská makra
    http://www.root.cz/clanky/programovaci-jazyk-clojure-16-slozitejsi-uzivatelska-makra/
  57. Clojure 17: Využití standardních maker v praxi
    http://www.root.cz/clanky/programovaci-jazyk-clojure-17-vyuziti-standardnich-maker-v-praxi/
  58. Clojure 18: Základní techniky optimalizace aplikací
    http://www.root.cz/clanky/programovaci-jazyk-clojure-18-zakladni-techniky-optimalizace-aplikaci/
  59. Clojure 19: Vývojová prostředí pro Clojure
    http://www.root.cz/clanky/programovaci-jazyk-clojure-19-vyvojova-prostredi-pro-clojure/
  60. Clojure 20: Vývojová prostředí pro Clojure (Vimu s REPL)
    http://www.root.cz/clanky/programovaci-jazyk-clojure-20-vyvojova-prostredi-pro-clojure-integrace-vimu-s-repl/
  61. Clojure 21: ClojureScript aneb překlad Clojure do JS
    http://www.root.cz/clanky/programovaci-jazyk-clojure-21-clojurescript-aneb-preklad-clojure-do-javascriptu/
Našli jste v článku chybu?
Podnikatel.cz: Dva měsíce na EET. Budou stačit?

Dva měsíce na EET. Budou stačit?

Lupa.cz: Další Češi si nechali vložit do těla čip

Další Češi si nechali vložit do těla čip

Lupa.cz: Adblock Plus začal prodávat reklamy

Adblock Plus začal prodávat reklamy

Lupa.cz: Jak levné procesory změnily svět?

Jak levné procesory změnily svět?

120na80.cz: Galerie: Čínští policisté testují českou minerálku

Galerie: Čínští policisté testují českou minerálku

Lupa.cz: Patička e-mailu závazná jako vlastnoruční podpis?

Patička e-mailu závazná jako vlastnoruční podpis?

Lupa.cz: Cimrman má hry na YouTube i vlastní doodle

Cimrman má hry na YouTube i vlastní doodle

Vitalia.cz: Test dětských svačinek: Tyhle ne!

Test dětských svačinek: Tyhle ne!

Podnikatel.cz: Udělali jsme velkou chybu, napsal Čupr

Udělali jsme velkou chybu, napsal Čupr

Vitalia.cz: dTest odhalil ten nejlepší kečup

dTest odhalil ten nejlepší kečup

DigiZone.cz: Budoucnost TV vysílání ve Visegrádu

Budoucnost TV vysílání ve Visegrádu

Vitalia.cz: Tradiční čínská medicína a rakovina

Tradiční čínská medicína a rakovina

Vitalia.cz: Tohle jsou nejlepší česká piva podle odborníků

Tohle jsou nejlepší česká piva podle odborníků

Lupa.cz: Jak se prodává firma za miliardu?

Jak se prodává firma za miliardu?

Vitalia.cz: 5 chyb, které děláme při skladování potravin

5 chyb, které děláme při skladování potravin

Vitalia.cz: Tahák, jak vyzrát nad zápachem z úst

Tahák, jak vyzrát nad zápachem z úst

Lupa.cz: Blíží se konec Wi-Fi sítí bez hesla?

Blíží se konec Wi-Fi sítí bez hesla?

Podnikatel.cz: Tyto pojmy k #EET byste měli znát

Tyto pojmy k #EET byste měli znát

Podnikatel.cz: Letáky? Lidi zuří, ale ony stále fungují

Letáky? Lidi zuří, ale ony stále fungují

DigiZone.cz: DVB-T2 ověřeno: seznam TV zveřejněn

DVB-T2 ověřeno: seznam TV zveřejněn