Obsah
1. Jazyky umožňující operace s poli aneb rozsáhlý svět „array programmingu“
2. Podpora n-rozměrných polí v programovacích jazycích a knihovnách
3. Jenže … co si vlastně představit pod jménem pole?
4. Rozdílný přístup k práci s poli v různých programovacích jazycích
5. Optimalizace operací s poli, paralelní výpočty a zpracování signálů
6. Příklady rozdílných přístupů programovacích jazyků při práci s poli
7. Vektory a matice v Dartmouth BASICu popř. v BBS BASICu
8. Pole v programovacím jazyce Julia
9. Vícerozměrná pole, sloupcové a řádkové vektory
10. Změna tvaru pole funkcí reshape
11. Jeden z důsledků typového systému jazyka Julia a měnitelnosti polí
12. N-rozměrná pole v LISPovské rodině programovacích jazyků
15. Infixová notace řešená přes makrosystém jazyka Clojure
17. Vektory v programovacím jazyku Kawa
18. N-rozměrná pole (ND-Array) v jazyku Kawa
1. Jazyky umožňující operace s poli aneb rozsáhlý svět „array programmingu“
V dnešním článku se začneme zabývat jednou poměrně rozsáhlou a současně i poněkud specifickou oblastí v informatice. Tou je zpracování vektorů, matic a taktéž vícerozměrných polí – obecně se v tomto kontextu mluví o n-rozměrných polích. S těmito velmi užitečnými datovými strukturami se můžeme setkat v různých (mnohdy zdánlivě i velmi vzdálených) disciplínách, například ve finančnictví, pojišťovnictví, statistice, zpracování numerických dat, simulacích, zpracování 1D a 2D signálů atd. Zapomenout ovšem nesmíme ani na strojové učení (machine learning) a umělou inteligencí (artifical intelligence), protože například datové struktury určené pro uložení neuronových sítí (zejména konvolučních sítí) jsou realizovány n-rozměrnými poli. 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). A pokud zůstaneme u 1D a 2D polí – zde došlo k rozšíření digitálních signálových procesorů orientovaných a optimalizovaných právě na tuto oblast.
Operace s poli jsou buď součástí syntaxe a sémantiky programovacích jazyků nebo jsou realizovány formou knihovny. Současné knihovny určené pro práci s n-rozměrnými 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 v některých případech 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 pak v této trojici článků:
- SIMD instrukce využívané v moderních mikroprocesorech řady x86
https://www.root.cz/clanky/simd-instrukce-vyuzivane-v-modernich-mikroprocesorech-rady-x86/ - SIMD instrukce v moderních mikroprocesorech řady x86 (2.část: SSE)
https://www.root.cz/clanky/simd-instrukce-v-modernich-mikroprocesorech-rady-x86–2-cast-sse/ - SIMD instrukce v moderních mikroprocesorech řady x86 (3.část: SSE2)
https://www.root.cz/clanky/simd-instrukce-v-modernich-mikroprocesorech-rady-x86–3-cast-sse2/
2. Podpora n-rozměrných polí v programovacích jazycích a knihovnách
Zmiňme se nyní o některých programovacích jazycích, v nichž manipulace s poli tvoří nedílnou součást takového jazyka (a určuje tak i oblast, v níž se daný programovací jazyk používá). Práce s vektory a maticemi byla (a samozřejmě doposud je) podporována zejména v překladačích FORTRANu, které začaly být po vzniku superpočítačů vybaveny specializovanými algoritmy. Tyto algoritmy dokázaly převést některé typy programových smyček na „vektorové operace“ (což ve skutečnosti byly operace aplikované na „konvoj“ prvků v hluboké pipeline). Paralelně k různým rozšířením FORTRANu ovšem vznikly i specializované jazyky určené téměř výhradně pro práci s vektory i maticemi. Velmi dobrým příkladem jsou programovací jazyky APL a J. I těmito neobvyklými (ale stále používanými!) programovacími jazyky jsme se na stránkách Rootu již zabývali, a to v následujících článcích:
- Programování mainframů: jazyk APL
https://www.root.cz/clanky/programovani-mainframu-jazyk-apl/ - Programovací jazyk APL: programování bez smyček
https://www.root.cz/clanky/programovaci-jazyk-apl-programovani-bez-smycek/ - Programovací jazyk APL – dokončení
https://www.root.cz/clanky/programovaci-jazyk-apl-dokonceni/ - Programovací jazyk J – od hieroglyfů k ASCII znakům
https://www.root.cz/clanky/programovaci-jazyk-j-ndash-od-hieroglyfu-k-nbsp-ascii-znakum/ - Programujeme v jazyku J: vektory a matice
https://www.root.cz/clanky/programujeme-v-jazyku-j-ndash-vektory-a-matice/ - Programovací jazyk J: operátory, uživatelské funkce a tacit programming
https://www.root.cz/clanky/programovaci-jazyk-j-operatory-uzivatelske-funkce-a-tacit-programming/ - Oslava 55 let od vzniku první implementace jazyka APL
https://www.root.cz/clanky/oslava-55-let-od-vzniku-prvni-implementace-programovaciho-jazyka-apl/
Velmi dobrou podporu pro práci s maticemi ovšem nabízí i framework Torch založený na programovacím jazyku Lua, s vektory a maticemi lze pracovat v programovacím jazyku Julia a zapomenout nesmíme ani na knihovnu Numpy. Ta je 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:
- Zpracování vektorů, matic a N-rozměrných polí v programovacím jazyku Kawa
https://www.root.cz/clanky/zpracovani-vektoru-matic-a-n-rozmernych-poli-v-programovacim-jazyku-kawa/ - Rust: knihovna ndarray pro práci s n-rozměrnými poli
https://www.root.cz/clanky/rust-knihovna-ndarray-pro-praci-s-n-rozmernymi-poli/ - Programovací jazyk Clojure a knihovny pro práci s vektory a maticemi
https://www.root.cz/clanky/programovaci-jazyk-clojure-a-knihovny-pro-praci-s-vektory-a-maticemi/ - Programovací jazyk Clojure a knihovny pro práci s vektory a maticemi (2)
https://www.root.cz/clanky/programovaci-jazyk-clojure-a-knihovny-pro-praci-s-vektory-a-maticemi-2/ - Gophernotes: kombinace interaktivního prostředí Jupyteru s jazykem Go
https://www.root.cz/clanky/gophernotes-kombinace-interaktivniho-prostredi-jupyteru-s-jazykem-go/ - Popis vybraných balíčků nabízených projektem Gonum
https://www.root.cz/clanky/popis-vybranych-balicku-nabizenych-projektem-gonum/ - Integrovaná vývojová prostředí ve Fedoře: vykreslování grafů s využitím knihoven Numpy a matplotlib
https://mojefedora.cz/integrovana-vyvojova-prostredi-ve-fedore-vykreslovani-grafu-s-vyuzitim-knihoven-numpy-a-matplotlib/ - Integrovaná vývojová prostředí ve Fedoře: praktické použití IPython Notebooku a knihovny Numpy (2.část)
https://mojefedora.cz/integrovana-vyvojova-prostredi-ve-fedore-prakticke-pouziti-ipython-notebooku-a-knihovny-numpy-2-cast/ - Integrovaná vývojová prostředí ve Fedoře: praktické použití IPython Notebooku a knihovny Numpy
https://mojefedora.cz/integrovana-vyvojova-prostredi-ve-fedore-prakticke-pouziti-ipython-notebooku-a-knihovny-numpy/
3. Jenže … co si vlastně představit pod jménem pole?
V programovacích jazycích se termín „pole“ resp. array používá velmi často, ovšem ani zdaleka ne konzistentně. V případě, že se v dokumentaci jazyka bez dalších podrobností termín array použije, je vhodné hledat odpovědi na následující otázky:
- Kolik dimenzí může pole mít? Typicky 1 a 2, někdy i více.
- Začínají indexy prvků od 0, 1 či je první index volitelný?
- Jsou podporována obdélníková pole nebo nepravidelná pole?
- Jsou jednotlivé osy na sobě nezávislé? (což vylučuje nepravidelná pole)
- Je možné indexy na jednotlivých osách pojmenovat? (a vytvořit tak vlastně datový rámec)
- Jedná se o homogenní nebo o heterogenní datovou strukturu? Homogenní struktura může uchovávat prvky jediného (typicky předem definovaného) typu zatímco v heterogenní struktuře mohou být umístěny prvky různých typů.
- Je nějakým způsobem omezen datový typ prvků pole? (například jen na celá čísla a čísla reálná).
- Lze prvky pole měnit (mutable) nebo je pole neměnitelné (immutable).
- Pokud jsou pole heterogenní a měnitelná, může prvek pole obsahovat to samé pole?
- Obsahuje pole přímo hodnoty prvků nebo jen reference na prvky?
- Jsou prvky v poli uloženy v operační paměti za sebou nebo se jedná o strukturu s ukazateli?
- Jsou prvky v 2D poli uloženy po řádcích nebo po sloupcích? (C versus Fortran).
- Lze měnit tvar (shape) pole?
- Podporuje jazyk operace nad celými poli?
- Podporuje jazyk takzvaný broadcasting (aplikaci skaláru na všechny prvky pole atd.)?
- Jsou pole plnohodnotným datovým typem nebo speciální strukturou?
- Je podporován „literál typu pole“?
4. Rozdílný přístup k práci s poli v různých programovacích jazycích
Různé programovací jazyky přistupují k implementaci polí rozdílným způsobem. Některé jazyky mají striktně omezen počet dimenzí na jednorozměrná pole a dvourozměrné matice. Mezi takové jazyky patří například osmibitové BASICy. Dále je možné pole reprezentovat jako jedinou souvislou oblast paměti (APL) s oddělenými metainformacemi o tvaru pole (shape); další jazyky reprezentují vícerozměrná pole jako pole polí (Java). U jazyků, kde jsou pole uložena v jediném souvislém bloku taktéž záleží na tom, zda je uložení provedeno po řádcích nebo po sloupcích (což je nejviditelnější na dvourozměrných polích). Zajímavým důsledkem oddělení metainformací o tvaru pole od prvků je možnost existence operace pro změnu tvaru pole, tj. například pro konverzi vektoru s 24 prvky na pole s 2×3×4 prvky atd. Poměrně velký rozdíl taktéž spočívá v tom, zda jsou pole homogenní či heterogenní. A pochopitelně samostatný problém představuje uložení pole do prvku sebe sama (což musí být detekováno a řešeno v runtime jazyka).
5. Optimalizace operací s poli, paralelní výpočty a zpracování signálů
V oblasti numerických simulací, což byl jeden z prvních úkolů, na které byly programovatelné počítače nasazeny, se často provádí operace s vektory a maticemi. Ostatně právě pro tento typ úkolů byl navržen programovací jazyk FORTRAN. A po vzniku superpočítačů začaly být překladače FORTRANu vybavovány algoritmy, které dokázaly převést některé typy programových smyček souvisejících s vektorovými a maticovými operacemi na „vektorové operace“. Paralelně vznikly i specializované jazyky určené téměř výhradně pro práci s vektory i maticemi – příkladem je již zmíněná dvojice APL a J. Touto problematikou (kde superpočítače předběhly dobu) se budeme podrobněji zabývat příště.
Operace s poli, přesněji řečeno většinou s jednorozměrnými vektory a maticemi, souvisí i se zpracováním signálů, což je oblast, pro kterou byly vyvinuty specializované čipy – DSP. První typy digitálních signálových procesorů, tj. DSP umístěných na jediném čipu, vznikly až na přelomu sedmdesátých a osmdesátých let minulého století, ovšem o zpracování číslicových signálů v reálném čase se inženýři a posléze i programátoři pokoušeli již dříve. První úspěšné a dá se říci i reálně použitelné systémy vznikly již na minipočítačích na počátku sedmdesátých let minulého století, ovšem vzhledem k poměrně vysoké ceně minipočítačů, jejich velikosti i spotřebě (nenechme se zmýlit předponou mini-, ta vyjadřovala cenu, velikost a výkonnost minipočítačů vzhledem k mainframům :-) byla oblast jejich nasazení omezená pouze na ty technologické provozy, kde nebyla k dispozici dostatečně robustní alternativa. Pokud nebylo možné minipočítače použít, používaly se buď signálové procesory sestavené z více čipů, nebo se namísto zpracování číslicových signálů používala mnohem starší, a nutno říci, že v dané době i propracovanější technologie – analogové počítače, které ovšem pracují na zcela jiném principu, než programovatelné číslicové počítače.
DSP byly optimalizovány na dvě operace – násobení (tj. obsahovaly rychlou násobičku) a na přístup k prvkům vektorů, což zasahuje do oblasti, které se věnujeme dnes. Efektivní adresace prvků vektorů umožnila například realizovat operaci konvoluce, korelace, výpočet FFT atd. Těmito specifickými oblastmi se budeme zabývat později.
6. Příklady rozdílných přístupů programovacích jazyků při práci s poli
Na třetí kapitolu, v níž jsme se zmínili o různých významech pojmu array, nyní navážeme, protože si ukážeme, jak s poli (ať již tento název konkrétně znamená cokoli) pracují různé programovací jazyky. Začneme původním Dartmouth BASICem a na něj navazujícím BBC BASICem, v němž je podpora pro práci s poli až překvapivě dobrá. Zapomenout pochopitelně nemůžeme na programovací jazyk Julia, který s poli pracuje odlišným způsobem (což je zapříčiněno mj. i typovým systémem tohoto jazyka). A ve druhé polovině článku si ukážeme práci s poli v LISPovských programovacích jazycích.
7. Vektory a matice v Dartmouth BASICu popř. v BBS BASICu
Podpora pro manipulace s maticemi (tedy s dvourozměrnými poli) se objevila v Dartmouth BASICu, což je prapředek všech ostatních variant programovacího jazyka BASIC. Ovšem v dalších BASICech, zejména v těch určených pro osmibitové domácí mikropočítače, práci s maticemi nenalezneme – a kupodivu ji nenalezneme ani v moderních BASICech (což ukazuje na neznalost historie :-). Podívejme se na základní podporu pro práci s maticemi v Darthmouth BASICu. Tento jazyk umožňuje zapsat maticové operace s využitím prefixu MAT, tedy následujícím způsobem:
DIM A(4),B(4),C(4) MAT A = 1 MAT B = 2 * A MAT C = A + B MAT PRINT A,B,C
V BBC BASICu jsou do určité míry podporována n-rozměrná pole. Jednorozměrné vektory se alokují a používají takto. Povšimněte si toho, že vektor V ve skutečnosti obsahuje jedenáct prvků:
10 REM 11 REM Alokace vektoru 12 REM 15 DIM V(10) 20 REM 21 REM Naplneni vektoru daty 22 REM 30 FOR I=0 TO 10 40 V(I) = I*1 50 NEXT I 60 REM 61 REM Vypis obsahu vektoru 62 REM 70 FOR I=0 TO 10 80 PRINT I,V(I) 90 NEXT I
Výsledek:
0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10
Pravidlo „n+1“ platí i pro dvojrozměrná pole neboli matice. Zajímavé je, že DIM jako příkaz alokuje pole a jako funkce vrací počet dimenzí (což do určité míry připomíná APL s monadickými a dyadickými operátory):
10 REM 11 REM Alokace pole 4x5 prvku 12 REM 20 DIM A(3,4) 30 PRINT DIM(A())
Výsledek získaný po spuštění tohoto příkladu:
2
Získat lze i velikosti pole v každé dimenzi, tedy rank, a to voláním funkce DIM se dvěma parametry – polem a indexem dimenze:
10 REM 11 REM Alokace pole 4x5 prvku 12 REM 20 DIM A(3,4) 30 PRINT DIM(A()) 40 REM 41 REM Vypis velikosti pole v kazde dimenzi 42 REM 50 FOR D=1 TO DIM(A()) 60 PRINT D,DIM(A(),D) 70 NEXT D
Výsledek (rank vrací hodnoty 3 a 4):
2 1 3 2 4
Počet dimenzí ve skutečnosti není omezen na 1 či 2; vytvořit lze například i trojrozměrné pole:
10 REM 11 REM Alokace pole 5x6x7 prvku 12 REM 20 DIM A(4,5,6) 30 PRINT DIM(A())
rank zjišťovaný pro trojrozměrná pole:
10 REM 11 REM Alokace pole 5x6x7 prvku 12 REM 20 DIM A(4,5,6) 30 PRINT DIM(A()) 40 REM 41 REM Vypis velikosti pole v kazde dimenzi 42 REM 50 FOR D=1 TO DIM(A()) 60 PRINT D,DIM(A(),D) 70 NEXT D
Výsledek:
3 1 4 2 5 3 6
Kupodivu není podporován tisk celého pole, takže si musíme pomoci dvojicí programových smyček (v případě trojrozměrného pole pak trojicí vnořených programových smyček):
10 REM 11 REM Alokace pole 4x5 prvku 12 REM 20 DIM A(3,4) 30 PRINT DIM(A()) 40 REM 41 REM Naplneni prvku pole 42 REM 50 FOR J=0 TO DIM(A(),2) 51 FOR I=0 TO DIM(A(),1) 52 A(I,J) = I*J 53 NEXT I 54 NEXT J 60 REM 61 REM Vypis obsahu pole 62 REM 70 FOR J=0 TO DIM(A(),2) 71 FOR I=0 TO DIM(A(),1) 72 PRINT A(I,J); 73 NEXT I 74 PRINT 75 NEXT J
Výsledek:
2 0 0 0 0 0 1 2 3 0 2 4 6 0 3 6 9 0 4 8 12
BBC Basic podporuje i operace nad celými poli, což je pro zdánlivě primitivní jazyk neobvyklé. Jednotlivé podporované operace jsou vypsány v následující dvojici tabulek. V první tabulce jsou binární operátory, jejichž levým parametrem je pole:
Levý operand | Operátor | Pravý operand | Stručný popis |
---|---|---|---|
pole | + | pole | součet prvek po prvku |
pole | – | pole | rozdíl prvek po prvku |
pole | * | pole | součin prvek po prvku |
pole | / | pole | podíl prvek po prvku |
pole | + | skalár | přičtení skaláru ke všem prvkům pole |
skalár | + | pole | přičtení skaláru ke všem prvkům pole |
pole | – | skalár | odečtení skaláru ke všem prvkům pole |
skalár | – | pole | odečtení skaláru od všech prvků pole |
pole | * | skalár | vynásobení všech prvků pole konstantou |
skalár | * | pole | vynásobení všech prvků pole konstantou |
pole | / | skalár | vydělení všech prvků pole konstantou |
skalár | / | pole | výpočet skalár/prvek |
pole | . | pole | maticový součin |
V tabulce druhé jsou vypsány operátory zkombinované s přiřazením:
Levá strana | Operátor | Pravá strana | Stručný popis |
---|---|---|---|
pole | = | pole | kopie celého pole |
pole | = | skalár | nastavení všech prvků pole na zadanou hodnotu |
pole | = | -pole | speciální případ, kopie celého pole s otočením znaménka všech prvků |
pole | += | skalár/pole | zkrácený zápis pole = pole + skalár/pole |
pole | -= | skalár/pole | zkrácený zápis pole = pole – skalár/pole |
Můžeme vidět, že některé operace bychom skutečně u „jednoduchého“ BASICu nečekali.
Ukázka operace pole = pole + skalár:
10 REM 11 REM Alokace pole 4x5 prvku 12 REM 20 DIM A(3,4) 30 PRINT DIM(A()) 40 REM 41 REM Naplneni prvku pole 42 REM 50 FOR J=0 TO DIM(A(),2) 51 FOR I=0 TO DIM(A(),1) 52 A(I,J) = I*J 53 NEXT I 54 NEXT J 60 REM 61 REM Operace s celym polem 62 REM 65 A() = A() + 1 70 REM 71 REM Vypis obsahu pole 72 REM 80 FOR J=0 TO DIM(A(),2) 81 FOR I=0 TO DIM(A(),1) 82 PRINT A(I,J); 83 NEXT I 84 PRINT 85 NEXT J
Výsledek:
2 1 1 1 1 1 2 3 4 1 3 5 7 1 4 7 10 1 5 9 13
Ukázka operace pole += skalár:
10 REM 11 REM Alokace pole 4x5 prvku 12 REM 20 DIM A(3,4) 30 PRINT DIM(A()) 40 REM 41 REM Naplneni prvku pole 42 REM 50 FOR J=0 TO DIM(A(),2) 51 FOR I=0 TO DIM(A(),1) 52 A(I,J) = I*J 53 NEXT I 54 NEXT J 60 REM 61 REM Operace s celym polem 62 REM 65 A() += 1 70 REM 71 REM Vypis obsahu pole 72 REM 80 FOR J=0 TO DIM(A(),2) 81 FOR I=0 TO DIM(A(),1) 82 PRINT A(I,J); 83 NEXT I 84 PRINT 85 NEXT J
Výsledek:
2 1 1 1 1 1 2 3 4 1 3 5 7 1 4 7 10 1 5 9 13
Vynásobení odpovídajících si prvků pole:
10 REM 11 REM Alokace pole 4x5 prvku 12 REM 20 DIM A(3,4) 30 PRINT DIM(A()) 40 REM 41 REM Naplneni prvku pole 42 REM 50 FOR J=0 TO DIM(A(),2) 51 FOR I=0 TO DIM(A(),1) 52 A(I,J) = I*J 53 NEXT I 54 NEXT J 60 REM 61 REM Operace s celym polem - nasobeni prvek po prvku 62 REM 65 A() *= A() 70 REM 71 REM Vypis obsahu pole 72 REM 80 FOR J=0 TO DIM(A(),2) 81 FOR I=0 TO DIM(A(),1) 82 PRINT A(I,J); 83 NEXT I 84 PRINT 85 NEXT J
Výsledek:
2 0 0 0 0 0 1 4 9 0 4 16 36 0 9 36 81 0 16 64 144
A konečně nejdelší příklad – maticový součin:
10 REM 11 REM Alokace prvniho pole 5x2 prvku 12 REM 20 DIM A(4,1) 21 PRINT DIM(A()) 30 REM 31 REM Naplneni prvku pole 32 REM 40 FOR J=0 TO DIM(A(),2) 41 FOR I=0 TO DIM(A(),1) 42 A(I,J) = I*J 43 NEXT I 44 NEXT J 50 REM 51 REM Alokace druheho pole 2x6 prvku 52 REM 60 DIM B(1,5) 61 PRINT DIM(B()) 70 REM 71 REM Naplneni prvku pole 72 REM 80 FOR J=0 TO DIM(B(),2) 81 FOR I=0 TO DIM(B(),1) 82 B(I,J) = I*J 83 NEXT I 84 NEXT J 90 REM 92 REM Maticovy soucin 92 REM 90 DIM C(4,5) 91 C() = A().B() 92 REM 93 REM Vypis obsahu pole 94 REM 95 FOR J=0 TO DIM(C(),2) 96 FOR I=0 TO DIM(C(),1) 97 PRINT C(I,J); 98 NEXT I 99 PRINT 100 NEXT J
Výsledek:
2 2 0 0 0 0 0 0 1 2 3 4 0 2 4 6 8 0 3 6 9 12 0 4 8 12 16 0 5 10 15 20
8. Pole v programovacím jazyce Julia
Dalším jazykem, který práci s vícerozměrnými poli podporuje na velmi dobré úrovni (i když s několika problematickými rysy), je programovací jazyk Julia. Základní homogenní datovou strukturou, kterou programovací jazyk Julia svým uživatelům nabízí, jsou jednorozměrná pole. Všechny prvky pole mají stejný typ (ostatně právě proto je to homogenní datová struktura a nikoli struktura heterogenní) a ke každému prvku je možné přistupovat přes jeho index, přičemž indexování prvků má konstantní složitost (nezáleží tedy na délce pole). Prvky v běžných jednorozměrných polích je možné modifikovat, takže pole jsou v jazyku Julia měnitelnými datovými strukturami (mutable). Podívejme se nyní na způsob vytvoření jednorozměrných polí v tomto programovacím jazyce:
julia> a=[1, 2, 3, 4, 5] 5-element Array{Int64,1}: 1 2 3 4 5
Při konstrukci pole se automaticky může zjistit datový typ prvků (resp. typ, který všem prvkům odpovídá po případné konverzi). Povšimněte si, jak se jazyk rozhoduje, který typ použít ve chvíli, kdy budeme chtít do pole uložit tři prvky různého typu:
julia> a=[1, 2.1, 1//3] 3-element Array{Float64,1}: 1.0 2.1 0.333333 julia> a=[1, 2, 1//3] 3-element Array{Rational{Int64},1}: 1//1 2//1 1//3 julia> a=[1/0, -1/0, 0/0] 3-element Array{Float64,1}: Inf -Inf NaN julia> a=[pi, pi] 2-element Array{Irrational{:π},1}: π = 3.1415926535897... π = 3.1415926535897...
Typ je ovšem v případě potřeby možné specifikovat explicitně:
julia> Int8[1, 2, 3, 4, 5] 5-element Array{Int8,1}: 1 2 3 4 5 julia> Float16[1, 2, 3, 4, 5] 5-element Array{Float16,1}: 1.0 2.0 3.0 4.0 5.0
V případě, že vynecháte čárky, vytvoří se ve skutečnosti dvourozměrné pole s jedním řádkem:
julia> a=[1 2 3 4 5] 1x5 Array{Int64,2}: 1 2 3 4 5 julia> Float16[1 2 3 4 5] 1x5 Array{Float16,2}: 1.0 2.0 3.0 4.0 5.0 julia> a=[1 2 3 4] 1x4 Array{Int64,2}: 1 2 3 4
9. Vícerozměrná pole, sloupcové a řádkové vektory
V předchozí kapitole jsme si ukázali způsob tvorby jednorozměrných polí v programovacím jazyku Julia. Jak se však vytváří dvourozměrná pole? První pokus, který může vycházet ze zkušeností z jiných programovacích jazyků (například z Pythonu), nebude příliš úspěšný:
julia> [[1,2,3], [4,5,6]] WARNING: [a,b] concatenation is deprecated; use [a;b] instead in depwarn at deprecated.jl:73 in oldstyle_vcat_warning at ./abstractarray.jl:29 in vect at abstractarray.jl:32 while loading no file, in expression starting on line 0 6-element Array{Int64,1}: 1 2 3 4 5 6
Problém v předchozím zápisu představovala čárka vložená mezi oba vektory. Jedno z možných řešení může vypadat takto – vytvoříme vlastně pole složené ze dvou sloupců (povšimněte si chybějící čárky mezi vektory):
julia> [[1,2,3] [4,5,6]] 3x2 Array{Int64,2}: 1 4 2 5 3 6
V případě, že preferujete zápis po řádcích, lze použít tento alternativní způsob se středníkem. Je to sice poněkud neobvyklé, ale středník zde nahrazuje volání funkce hvcat() zmíněné níže:
julia> a=[1 2; 3 4] 2x2 Array{Int64,2}: 1 2 3 4
Pole se dvěma řádky a třemi sloupci se tedy vytvoří následovně:
julia> [1 2 3 ; 3 4 5] 2x3 Array{Int64,2}: 1 2 3 3 4 5
Kromě zápisu prvků pole do hranatých závorek lze pro konstrukci použít i funkce hcat (což znamená „horizontal concatenate“), vcat („vertical concatenate“) a hvcat (kombinace obou možností se specifikací počtu sloupců):
julia> hcat(1,2,3,4) 1x4 Array{Int64,2}: 1 2 3 4
julia> vcat(1,2,3,4) 4-element Array{Int64,1}: 1 2 3 4
U funkce hvcat() si povšimněte, že první parametr specifikuje počet sloupců a až po něm následují jednotlivé prvky, což může být poněkud matoucí:
julia> hvcat(2,1,2,3,4) 2x2 Array{Int64,2}: 1 2 3 4
Jednosloupcové pole:
julia> hvcat(1,1,2,3,4) 4x1 Array{Int64,2}: 1 2 3 4
Čtyřsloupcové pole (s jedním řádkem):
julia> hvcat(4,1,2,3,4) 1x4 Array{Int64,2}: 1 2 3 4
Pro vytvoření pole s udáním typu prvků (ovšem bez inicializace jednotlivých prvků) slouží konstruktor nazvaný jednoduše Array. Při volání tohoto konstruktoru se nejprve ve složených závorkách specifikuje typ prvků a již běžně v kulatých závorkách pak rozměry pole v jednotlivých dimenzích:
help?> Array search: Array SubArray BitArray DenseArray StridedArray mmap_array Array(dims) Array{T}(dims) constructs an uninitialized dense array with element type T. dims may be a tuple or a series of integer arguments. The syntax Array(T, dims) is also available, but deprecated.
Konstrukce pole o rozměrech 2×2 prvky typu Int8 (osmibitové celé číslo se znaménkem) se provede takto:
julia> a=Array{Int8}(2,2) 2x2 Array{Int8,2}: 112 -26 82 -34
Zkusme nyní změnit hodnotu prvku v poli 10×10 prvků:
julia> a[0,0]=42 ERROR: LoadError: BoundsError: attempt to access 2×2 Array{Int8,2} at index [0, 0] Stacktrace: [1] setindex!(::Array{Int8,2}, ::Int64, ::Int64, ::Int64) at ./array.jl:550 while loading /home/cg/root/5386118/main.jl, in expression starting on line 3
Vidíme, že se tato operace nepodařila, a to z toho důvodu, že se prvky indexují od jedničky a nikoli od nuly. To se sice může zdát poněkud neobvyklé, ovšem ve skutečnosti mnoho jazyků (dovolím si dokonce říci, že většina jazyků NEodvozených od céčka) zvolilo stejný přístup: Fortran, Mathematica, R, MATLAB, Lua atd. Správně tedy má příkaz vypadat takto:
julia> a[1,1]=42 42
Prvek pole se skutečně změnil:
julia> a 10x10 Array{Float16,2}: 42.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.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.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.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.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.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.0 0.0 0.0 0.0
Podívejme se nyní na složitější indexování, tentokrát vektoru:
julia> v=[1 2 3 4 10 -1] 1x6 Array{Int64,2}: 1 2 3 4 10 -1
Přístup k prvnímu prvku:
julia> v[1] 1
Přístup k prvkům 2 až 4:
julia> v[2:4] 3-element Array{Int64,1}: 2 3 4
Přístup ke všem prvkům vektoru:
julia> v[:] 6-element Array{Int64,1}: 1 2 3 4 10 -1
Pokud potřebujeme přistoupit k poslednímu prvku, není možné použít index –1 (to lze v jiných jazycích), ale používá se zde slovo end. To opět není nijak unikátní, podobně se toto slovo používá i v MATLABu:
julia> v[end] -1
Kombinace předchozích způsobů – od čtvrtého prvku do konce vektoru:
julia> v[4:end] 3-element Array{Int64,1}: 4 10 -1
Zajímavý je výběr sekvence libovolných prvků vektoru (pole), a to s využitím jiného vektoru obsahujícího indexy prvků. Povšimněte si nutnosti použití dvojitých hranatých závorek – vnější závorky představují operaci výběru prvků, vnitřní závorky vektor indexů:
julia> v[[1,5,6,2,5,5]] 6-element Array{Int64,1}: 1 10 -1 2 10 10
10. Změna tvaru pole funkcí reshape
Další velmi důležitou funkcí, s níž se v praxi často setkáme, je funkce nazvaná reshape(), která dokáže změnit velikost matice a vhodným způsobem přeorganizovat prvky v původní matici (převzato z APL – jak jinak). Této funkci se předávají dva parametry – prvním parametrem je vstupní pole (vektor, matice, …), druhým parametrem (popř. více parametry) pak specifikace tvaru výsledného pole. Podívejme se nejdříve na oficiální popis této funkce:
help?> reshape search: reshape promote_shape reshape(A, dims) Create an array with the same data as the given array, but with different dimensions. An implementation for a particular type of array may choose whether the data is copied or shared.
Vytvořme si testovací vektor s dvanácti prvky (což je číslo dělitelné 2, 3, 4 i 6):
julia> a=[1 2 3 4 5 6 7 8 9 10 11 12] 1x12 Array{Int64,2}: 1 2 3 4 5 6 7 8 9 10 11 12
Z tohoto vektoru pak snadno získáme matice o rozměrech 4×3, 3×4, 2×6, 6×2 či 1×12:
julia> reshape(a, 4, 3) 4x3 Array{Int64,2}: 1 5 9 2 6 10 3 7 11 4 8 12
julia> reshape(a, 3, 4) 3x4 Array{Int64,2}: 1 4 7 10 2 5 8 11 3 6 9 12
julia> reshape(a, 2, 6) 2x6 Array{Int64,2}: 1 3 5 7 9 11 2 4 6 8 10 12
julia> reshape(a, 6, 2) 6x2 Array{Int64,2}: 1 7 2 8 3 9 4 10 5 11 6 12
Vytvoření trojrozměrných polí je stejně snadné (a opět je nutné zachovat počet prvků):
julia> reshape(a, 2, 3, 2) 2x3x2 Array{Int64,3}: [:, :, 1] = 1 3 5 2 4 6 [:, :, 2] = 7 9 11 8 10 12
11. Jeden z důsledků typového systému jazyka Julia a měnitelnosti polí
Víme již, že v jazyku Julia se typ pole odvozuje od prvků, které jsou do pole přiřazeny. Pole celých čísel se tedy vytvoří a použije takto:
a = [1,2,3] println(a) a[2] = -1 println(a)
Pole je sice homogenním datovým typem, ovšem typový systém jazyka Julia umožňuje vytvořit pole prvků typu Any, což může být další pole:
a = [1,[],3] println(a) -> Any[1, Any[], 3] a[2] = [10,10] println(a) -> Any[1, [10, 10], 3]
Takže – pole jsou měnitelné datové struktury a mohou obsahovat prvky typu Any. To ovšem znamená, že prvkem pole může být to samé pole!
a[2] = a println(a)
S výsledkem:
Any[1, Any[#= circular reference @-1 =#], 3]
Dtto při přístupu do (nekonečně zanořené) struktury:
println(a[2]) Any[1, Any[#= circular reference @-1 =#], 3]
12. N-rozměrná pole v LISPovské rodině programovacích jazyků
Manipulace s N-rozměrnými poli jsou zdánlivě vyhrazeny pouze programovacím jazykům odvozeným od FORTRANu. To ovšem ve skutečnosti není pravda, protože se poměrně často můžeme setkat s mnohdy velmi vyspělými knihovnami pro práci s těmito datovými typy, které jsou určeny pro LISPovské programovací jazyky. V dalším textu si ve stručnosti popíšeme knihovny resp. přesněji řečeno podporu pro práci s n-rozměrnými poli v jazyku Clojure a taktéž v programovacím jazyku Kawa.
V přednášce nazvané velmi příhodně „Enter the Matrix“, která je dostupná na adrese http://www.slideshare.net/mikeranderson/2013–1114-enter-thematrix, je mj. ukázáno, jakým způsobem jsou v Clojure implementována různá paradigmata programování. Díky podpoře maker a způsobu zápisu programového kódu v Clojure lze velmi snadno implementovat různé doménově specifické jazyky (DSL), mj. i právě jazyk pro array programming:
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 |
13. Knihovna core.matrix
Nejprve se ve stručnosti seznámíme s knihovnou určenou pro práci s n-dimenzionálními poli v programovacím jazyku Clojure. Konkrétně se bude jednat o knihovnu nazvanou core.matrix. Tato knihovna je určená těm vývojářům, kteří ve svých projektech potřebují provádět velké množství operací s maticemi různých dimenzí, a to na poměrně vysoké úrovni, tj. bez nutnosti přesně specifikovat, jakým způsobem mají být matice uloženy v paměti, jakým způsobem se má provádět operace 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 jazyku APL, až na ten rozdíl, že algoritmy zapisované v Clojure jsou pro většinu vývojářů přece jen čitelnější :-). Taktéž je důležité, že rozhraní definované v knihovně core.matrix může mít několik implementací. V současnosti se jedná o implementace poskytované knihovnami 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 nepotřebuje).
Příklady vybraných operací s 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=> (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]]]
Získání informací o matici:
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]
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]]
14. Projekt Incanter
Projekt Incanter, jenž je taktéž určen pro jazyk Clojure, je založen na zpracování (rozsáhlých) matic a tzv. datasetů. Ve skutečnosti však klasicky chápané matice v Clojure příliš podporovány nejsou. Při studiu základních knihoven Clojure lze 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í. 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í specializovaných knihoven. Projektem Incanter je proto interně používána knihovna core.matrix, která byla ve stručnosti představena v předchozí kapitole.
15. Infixová notace řešená přes makrosystém jazyka Clojure
Incanter je sice knihovna (resp. skupina knihoven) určená pro programovací jazyk Clojure, který je založen na lispovské syntaxi volání funkcí, ovšem díky existenci makra $= je možné výrazy zapisovat i v infixové podobě, a to včetně správného vyhodnocení priorit operátorů. Podívejme se na několik příkladů.
Základní aritmetické operátory s vyhodnocením priorit:
incanter.irepl=> ($= 1 + 2) 3 incanter.irepl=> ($= 1 + 2 * 3) 7
Zpracovat lze dokonce i vektory a matice (operátory jsou přetížené):
incanter.irepl=> ($= [1 2 3] + [4 5 6]) [5 7 9] incanter.irepl=> ($= [1 2 3] * [4 5 6]) [4 10 18] incanter.irepl=> ($= [[1 2][3 4]] + [[5 6][7 8]]) [[6 8] [10 12]]
Využití broadcastingu zmíněného v další kapitole:
incanter.irepl=> ($= [1 2 3] + 10) [11 12 13] incanter.irepl=> ($= [1 2 3] * -1) [-1 -2 -3]
16. Broadcasting
Podívejme se nyní na některé základní operace s maticemi, zejména na takzvaný broadcasting:
incanter.irepl=> (def A (matrix [[1 2 3] #_=> [4 5 6] #_=> [7 8 9]])) #'incanter.irepl/A
Broadcasting umožňuje převést číslo (přesněji řečeno skalár) na matici stejného řádu, jakou má druhý operand:
incanter.irepl=> ($= A + 2) #vectorz/matrix [[3.0,4.0,5.0], [6.0,7.0,8.0], [9.0,10.0,11.0]]
Vynásobení matice konstantou (skalárem):
incanter.irepl=> ($= A * -1) #vectorz/matrix [[-1.0,-2.0,-3.0], [-4.0,-5.0,-6.0], [-7.0,-8.0,-9.0]]
Vynásobení matice a vektoru, který byl opět rozšířen na matici:
incanter.irepl=> ($= A * [5 0 5]) #vectorz/matrix [[5.0,0.0,15.0], [20.0,0.0,30.0], [35.0,0.0,45.0]]
Skutečný „maticový“ součin matice a vektoru:
incanter.irepl=> (mmult A [5 0 0]) #vectorz/vector [5.0,20.0,35.0]
Matici můžeme transponovat:
incanter.irepl=> (trans A) #vectorz/matrix [[1.0,4.0,7.0], [2.0,5.0,8.0], [3.0,6.0,9.0]]
Můžeme provést vynásobení původní matice s maticí transponovanou:
incanter.irepl=> (mmult A (trans A)) #vectorz/matrix [[14.0,32.0,50.0], [32.0,77.0,122.0], [50.0,122.0,194.0]]
Další složitější varianta:
incanter.irepl=> (mmult A (trans A) A) #vectorz/matrix [[492.0,588.0,684.0], [1194.0,1425.0,1656.0], [1896.0,2262.0,2628.0]]
17. Vektory v programovacím jazyku Kawa
Dalším LISPovským jazykem je Kawa určená pro běh ve virtuálním stroji Javy. Klasická 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 (homogenita). Tato vlastnost (což je 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 využít 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 ve stručnosti ukážeme práci s takzvanými vektory. Podporovány jsou tyto funkce:
# | 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 je možné zkonstruovat speciálním „konstruktorem“, v němž se jednotlivé prvky vektoru zapisují do hranatých závorek (což již známe z programovacího jazyka Clojure). K prvkům vektoru se přistupuje funkcí vector-ref:
(define vector1 [1 2 3 4]) (display vector1) (newline) (display (vector-ref vector1 0)) (display (vector-ref vector1 10))
Výsledek:
#(1 2 3 4) java.lang.ArrayIndexOutOfBoundsException: 10 at gnu.lists.FVector.get(FVector.java:105) at kawa.lib.vectors.vectorRef(vectors.scm:21) at Vectors1.run(Vectors1.scm:7) at gnu.expr.ModuleExp.evalModule2(ModuleExp.java:289) at gnu.expr.CompiledModule.evalModule(CompiledModule.java:42) at gnu.expr.CompiledModule.evalModule(CompiledModule.java:60) at kawa.Shell.runFile(Shell.java:565) at kawa.Shell.runFileOrClass(Shell.java:468) at kawa.repl.processArgs(repl.java:700) at kawa.repl.main(repl.java:820)
Vektory lze ovšem 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 demonstrační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? (všechny predikáty končí otazníkem):
#|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
18. N-rozměrná pole (ND-Array) v jazyku Kawa
V této kapitole se ve stručnost seznámíme s možnostmi typu array, což je v jazyce Kawa datový typ představující N-rozměrná pole (neboli nosné téma dnešního článku). 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 (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í.
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 dalším textu. Funkci make-array si můžeme velmi snadno otestovat přímo v interaktivní smyčce REPL programovacího jazyka Kawa.
Mezním případem je prázdné pole:
#|kawa:3|# (make-array [0]) #()
Konstrukce jednoprvkového jednorozměrné pole vypadá takto:
#|kawa:8|# (make-array [1]) #(#!null)
Desetiprvkový vektor, jehož všechny prvky mají výchozí hodnotu:
#|kawa:5|# (make-array [10]) #(#!null #!null #!null #!null #!null #!null #!null #!null #!null #!null)
Konstrukce matice typu 1×1, tedy matice s jediným prvkem, který má výchozí hodnotu:
#|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║ ╚══════╧══════╝
Pokračujme s nepatrně složitější maticí, konkrétně s maticí se dvěma řádky a třemi sloupci:
#|kawa:10|# (make-array [2 3]) ╔#2a:2:3══════╤══════╗ ║#!null│#!null│#!null║ ╟──────┼──────┼──────╢ ║#!null│#!null│#!null║ ╚══════╧══════╧══════╝
Trojrozměrná struktura 2×3×4 prvky:
#|kawa:11|# (make-array [2 3 4]) ╔#3a:2:3:4════╤══════╤══════╗ ║#!null│#!null│#!null│#!null║ ╟──────┼──────┼──────┼──────╢ ║#!null│#!null│#!null│#!null║ ╟──────┼──────┼──────┼──────╢ ║#!null│#!null│#!null│#!null║ ╠══════╪══════╪══════╪══════╣ ║#!null│#!null│#!null│#!null║ ╟──────┼──────┼──────┼──────╢ ║#!null│#!null│#!null│#!null║ ╟──────┼──────┼──────┼──────╢ ║#!null│#!null│#!null│#!null║ ╚══════╧══════╧══════╧══════╝
Taktéž trojrozměrná struktura, ovšem tentokrát s tvarem 4×3×2 prvky:
#|kawa:12|# (make-array [4 3 2]) ╔#3a:4:3:2════╗ ║#!null│#!null║ ╟──────┼──────╢ ║#!null│#!null║ ╟──────┼──────╢ ║#!null│#!null║ ╠══════╪══════╣ ║#!null│#!null║ ╟──────┼──────╢ ║#!null│#!null║ ╟──────┼──────╢ ║#!null│#!null║ ╠══════╪══════╣ ║#!null│#!null║ ╟──────┼──────╢ ║#!null│#!null║ ╟──────┼──────╢ ║#!null│#!null║ ╠══════╪══════╣ ║#!null│#!null║ ╟──────┼──────╢ ║#!null│#!null║ ╟──────┼──────╢ ║#!null│#!null║ ╚══════╧══════╝
Čtyřrozměrné pole:
#|kawa:7|# (make-array [2 2 2 2]) ╔#4a:2:2:2:2══╗ ║#!null│#!null║ ╟──────┼──────╢ ║#!null│#!null║ ╠══════╪══════╣ ║#!null│#!null║ ╟──────┼──────╢ ║#!null│#!null║ ╠══════╪══════╣ ║#!null│#!null║ ╟──────┼──────╢ ║#!null│#!null║ ╠══════╪══════╣ ║#!null│#!null║ ╟──────┼──────╢ ║#!null│#!null║ ╚══════╧══════╝
Pole, které má v jedné dimenzi nulovou velikost a celkově tedy nula prvků:
#|kawa:11|# (make-array [0 2 2 2]) #4a:0:2:2:2 ()
Funkci make-arrray, s jejím základním použitím jsme se seznámili v předchozím textu, 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║ ╚═╧═╧═╝
Při inicializaci polí se používá konstrukce 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] #()
#|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║ ╚══╧══╧══╧══╝
19. Obsah navazujícího článku
V navazujícím článku se zaměříme na vybrané „klasické“ jazyky určené pro manipulaci s poli. Jedná se především o FORTRAN (dnes vlastně již psán „Fortran“), dále o jazyky APL a J doplněné o novinku představovanou jazykem BQN a taktéž se zmíníme o dnes již prakticky neznámém projektu VectorPascal. V dalších dílech si pak ukážeme některé možnosti jazyka Fortress.
20. Odkazy na Internetu
- What is an Array?
https://vector.org.uk/what-is-an-array/ - Vector (Wolfram MathWorld)
https://mathworld.wolfram.com/Vector.html - n-Tuple (Wolfram MathWorld)
https://mathworld.wolfram.com/n-Tuple.html - n-Vector (Wolfram MathWorld)
https://mathworld.wolfram.com/n-Vector.html - Matrix (Wolfram MathWorld)
https://mathworld.wolfram.com/Matrix.html - Array (Wolfram MathWorld)
https://mathworld.wolfram.com/Array.html - ND Arrays (Tensors) in different languages
https://www.youtube.com/watch?v=WbpbEilgQBc - Extending APL to Infinity\
https://www.jsoftware.com/papers/eem/infinity.htm - Vector Library (R7RS-compatible)
https://srfi.schemers.org/srfi-133/srfi-133.html - Vectors (pro Gauche)
https://practical-scheme.net/gauche/man/gauche-refe/Vectors.html - Kawa: Compiling Scheme to Java
https://www.mit.edu/afs.new/sipb/project/kawa/doc/kawa-tour.html - Kawa in Languages shootout
http://per.bothner.com/blog/2010/Kawa-in-shootout/ - Kawa 2.0 Supports Scheme R7RS
https://developers.slashdot.org/story/14/12/13/2259225/kawa-20-supports-scheme-r7rs/ - Kawa — fast scripting on the Java platform
https://lwn.net/Articles/623349/ - Incanter is a Clojure-based, R-like platform for statistical computing and graphics.
http://incanter.org/ - Evolution of incanter (Gource Visualization)
https://www.youtube.com/watch?v=TVfL5nPELr4 - Questions tagged [incanter] (na Stack Overflow)
https://stackoverflow.com/questions/tagged/incanter?sort=active - Data Sorcery with Clojure
https://data-sorcery.org/contents/ - Back to the Future: Lisp as a Base for a Statistical Computing System
https://rd.springer.com/chapter/10.1007/978–3–7908–2084–3_2 - Incanter Cheat Sheet
http://incanter.org/docs/incanter-cheat-sheet.pdf - Back to the Future: Lisp as a Base for a Statistical Computing System (celá verze článku)
https://www.researchgate.net/publication/227019917_Back_to_the_Future_Lisp_as_a_Base_for_a_Statistical_Computing_System - BQN: finally, an APL for your flying saucer
https://mlochbaum.github.io/BQN/ - Is BQN stable?
https://mlochbaum.github.io/BQN/commentary/stability.html - Specification: BQN system-provided values
https://mlochbaum.github.io/BQN/spec/system.html - Tutorial: BQN expressions
https://mlochbaum.github.io/BQN/tutorial/expression.html - BQN primitives
https://mlochbaum.github.io/BQN/doc/primitive.html - Function trains
https://mlochbaum.github.io/BQN/doc/train.html - BQN community links
https://mlochbaum.github.io/BQN/community/index.html - BQN UV
https://observablehq.com/@lsh/bqn-uv - APL Wiki
https://aplwiki.com/wiki/ - The Array Cast
https://www.arraycast.com/episodes/episode-03-what-is-an-array - EnthusiastiCon 2019 – An Introduction to APL
https://www.youtube.com/watch?v=UltnvW83_CQ - Dyalog
https://www.dyalog.com/ - Try APL!
https://tryapl.org/ - Lisp-Stat Information
http://homepage.cs.uiowa.edu/~luke/xls/xlsinfo/ - Sample Plots in Incanter
https://github.com/incanter/incanter/wiki/Sample-Plots-in-Incanter#line - vectorz-clj
https://github.com/mikera/vectorz-clj - vectorz – Examples
https://github.com/mikera/vectorz-clj/wiki/Examples - Basic Vector and Matrix Operations in Julia: Quick Reference and Examples
https://queirozf.com/entries/basic-vector-and-matrix-operations-in-julia-quick-reference-and-examples - Vectors and matrices in Julia
https://fncbook.github.io/v1.0/linsys/demos/matrices-julia.html - Array vs Matrix in R Programming
https://www.geeksforgeeks.org/array-vs-matrix-in-r-programming/ - Concurrency (computer science)
https://en.wikipedia.org/wiki/Category:Concurrency_%28computer_science%29 - Koprogram
https://cs.wikipedia.org/wiki/Koprogram - Coroutine
https://en.wikipedia.org/wiki/Coroutine - Coroutines in C
http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html - S-expression (Wikipedia)
https://en.wikipedia.org/wiki/S-expression - S-Expressions (Rosetta Code)
http://rosettacode.org/wiki/S-Expressions - Introducing Julia/Metaprogramming
https://en.wikibooks.org/wiki/Introducing_Julia/Metaprogramming - Tutorial for the Common Lisp Loop Macro
http://www.ai.sri.com/pkarp/loop.html - Clojure Macro Tutorial (Part I, Getting the Compiler to Write Your Code For You)
http://www.learningclojure.com/2010/09/clojure-macro-tutorial-part-i-getting.html - Clojure Macro Tutorial (Part II: The Compiler Strikes Back)
http://www.learningclojure.com/2010/09/clojure-macro-tutorial-part-ii-compiler.html - Clojure Macro Tutorial (Part III: Syntax Quote)
http://www.learningclojure.com/2010/09/clojure-macro-tutorial-part-ii-syntax.html - Clojure Macros and Metaprogramming
http://clojure-doc.org/articles/language/macros.html - Fatvat – Exploring functional programming: Clojure Macros
http://www.fatvat.co.uk/2009/02/clojure-macros.html - CS 2101 Parallel Computing with Julia
https://www.coursehero.com/file/11508091/CS-2101-Parallel-Computing-with-Julia/ - Julia By Example
https://samuelcolvin.github.io/JuliaByExample/ - Array Programming
https://en.wikipedia.org/wiki/Array_programming - Discovering Array Languages
http://archive.vector.org.uk/art10008110 - no stinking loops – Kalothi
http://www.nsl.com/ - Vector (obsahuje odkazy na články, knihy a blogy o programovacích jazycích APL, J a K)
http://www.vector.org.uk/ - APL Interpreters
http://www.vector.org.uk/?area=interpreters - APL_(programming_language
http://en.wikipedia.org/wiki/APL_(programming_language - APL FAQ
http://www.faqs.org/faqs/apl-faq/ - APL FAQ (nejnovější verze)
http://home.earthlink.net/~swsirlin/apl.faq.html - A+
http://www.aplusdev.org/ - APLX
http://www.microapl.co.uk/ - FreeAPL
http://www.pyr.fi/apl/index.htm - J: a modern, high-level, general-purpose, high-performance programming language
http://www.jsoftware.com/ - K, Kdb: an APL derivative for Solaris, Linux, Windows
http://www.kx.com - openAPL (GPL)
http://sourceforge.net/projects/openapl - Parrot APL (GPL)
http://www.parrotcode.org/ - Learning J (Roger Stokes)
http://www.jsoftware.com/help/learning/contents.htm - Rosetta Code
http://rosettacode.org/wiki/Main_Page - Why APL
http://www.acm.org/sigapl/whyapl.htm - Introducing Julia/Functions
https://en.wikibooks.org/wiki/Introducing_Julia/Functions - Functions (Julia documentation)
https://docs.julialang.org/en/v1/manual/functions/ - Evaluate binomial coefficients
http://rosettacode.org/wiki/Evaluate_binomial_coefficients - Ackermann function
http://rosettacode.org/wiki/Ackermann_function - Julia (front page)
http://julialang.org/ - Julia – dokumentace
http://docs.julialang.org/ - Julia – repositář na GitHubu
https://github.com/JuliaLang/julia - Julia (programming language)
https://en.wikipedia.org/wiki/Julia_%28programming_language%29 - IJulia
https://github.com/JuliaLang/IJulia.jl - Introducing Julia
https://en.wikibooks.org/wiki/Introducing_Julia - Julia: the REPL
https://en.wikibooks.org/wiki/Introducing_Julia/The_REPL - Month of Julia
https://github.com/DataWookie/MonthOfJulia - Learn X in Y minutes (where X=Julia)
https://learnxinyminutes.com/docs/julia/ - New Julia language seeks to be the C for scientists
http://www.infoworld.com/article/2616709/application-development/new-julia-language-seeks-to-be-the-c-for-scientists.html - Julia: A Fast Dynamic Language for Technical Computing
http://karpinski.org/publications/2012/julia-a-fast-dynamic-language - The LLVM Compiler Infrastructure
http://llvm.org/ - Julia: benchmarks
http://julialang.org/benchmarks/ - Type system
https://en.wikipedia.org/wiki/Type_system - Half-precision floating-point format
https://en.wikipedia.org/wiki/Half-precision_floating-point_format - Dartmouth BASIC
https://en.wikipedia.org/wiki/Dartmouth_BASIC - BASIC 4th Edition
http://www.bitsavers.org/pdf/dartmouth/BASIC_4th_Edition_Jan68.pdf - VECTRAN
https://encyclopedia2.thefreedictionary.com/VECTRAN - Comparison of programming languages (array)
https://en.wikipedia.org/wiki/Comparison_of_programming_languages_(array) - BASIC at 50
https://www.dartmouth.edu/basicfifty/commands.html - BBC Basic – arrays
http://www.riscos.com/support/developers/bbcbasic/part2/arrays.html - Datová struktura
https://cs.wikipedia.org/wiki/Datov%C3%A1_struktura - SIMD instrukce využívané v moderních mikroprocesorech řady x86
https://www.root.cz/clanky/simd-instrukce-vyuzivane-v-modernich-mikroprocesorech-rady-x86/ - SIMD instrukce v moderních mikroprocesorech řady x86 (2.část: SSE)
https://www.root.cz/clanky/simd-instrukce-v-modernich-mikroprocesorech-rady-x86–2-cast-sse/ - SIMD instrukce v moderních mikroprocesorech řady x86 (3.část: SSE2)
https://www.root.cz/clanky/simd-instrukce-v-modernich-mikroprocesorech-rady-x86–3-cast-sse2/ - Programování mainframů: jazyk APL
https://www.root.cz/clanky/programovani-mainframu-jazyk-apl/ - Programovací jazyk APL: programování bez smyček
https://www.root.cz/clanky/programovaci-jazyk-apl-programovani-bez-smycek/ - Programovací jazyk APL – dokončení
https://www.root.cz/clanky/programovaci-jazyk-apl-dokonceni/ - Programovací jazyk J – od hieroglyfů k ASCII znakům
https://www.root.cz/clanky/programovaci-jazyk-j-ndash-od-hieroglyfu-k-nbsp-ascii-znakum/ - Programujeme v jazyku J: vektory a matice
https://www.root.cz/clanky/programujeme-v-jazyku-j-ndash-vektory-a-matice/ - Programovací jazyk J: operátory, uživatelské funkce a tacit programming
https://www.root.cz/clanky/programovaci-jazyk-j-operatory-uzivatelske-funkce-a-tacit-programming/ - Oslava 55 let od vzniku první implementace jazyka APL
https://www.root.cz/clanky/oslava-55-let-od-vzniku-prvni-implementace-programovaciho-jazyka-apl/ - Zpracování vektorů, matic a N-rozměrných polí v programovacím jazyku Kawa
https://www.root.cz/clanky/zpracovani-vektoru-matic-a-n-rozmernych-poli-v-programovacim-jazyku-kawa/ - Rust: knihovna ndarray pro práci s n-rozměrnými poli
https://www.root.cz/clanky/rust-knihovna-ndarray-pro-praci-s-n-rozmernymi-poli/ - Programovací jazyk Clojure a knihovny pro práci s vektory a maticemi
https://www.root.cz/clanky/programovaci-jazyk-clojure-a-knihovny-pro-praci-s-vektory-a-maticemi/ - Programovací jazyk Clojure a knihovny pro práci s vektory a maticemi (2)
https://www.root.cz/clanky/programovaci-jazyk-clojure-a-knihovny-pro-praci-s-vektory-a-maticemi-2/ - Gophernotes: kombinace interaktivního prostředí Jupyteru s jazykem Go
https://www.root.cz/clanky/gophernotes-kombinace-interaktivniho-prostredi-jupyteru-s-jazykem-go/ - Popis vybraných balíčků nabízených projektem Gonum
https://www.root.cz/clanky/popis-vybranych-balicku-nabizenych-projektem-gonum/ - Integrovaná vývojová prostředí ve Fedoře: vykreslování grafů s využitím knihoven Numpy a matplotlib
https://mojefedora.cz/integrovana-vyvojova-prostredi-ve-fedore-vykreslovani-grafu-s-vyuzitim-knihoven-numpy-a-matplotlib/ - Integrovaná vývojová prostředí ve Fedoře: praktické použití IPython Notebooku a knihovny Numpy (2.část)
https://mojefedora.cz/integrovana-vyvojova-prostredi-ve-fedore-prakticke-pouziti-ipython-notebooku-a-knihovny-numpy-2-cast/ - Integrovaná vývojová prostředí ve Fedoře: praktické použití IPython Notebooku a knihovny Numpy
https://mojefedora.cz/integrovana-vyvojova-prostredi-ve-fedore-prakticke-pouziti-ipython-notebooku-a-knihovny-numpy/