Obsah
1. Programovací jazyk Rust: knihovna ndarray pro práci s n-rozměrnými poli (dokončení)
2. Funkce vyššího řádu map a jednorozměrná pole
3. Předávání prvků hodnotou a referencí, automatická dereference prvků
4. Funkce vyššího řádu map a dvourozměrná i trojrozměrná pole
5. Složitější příklad: vektory a pole obsahující komplexní čísla
6. Funkce vyššího řádu map a pole komplexních čísel
7. Přímá modifikace prvků pole pomocí funkce mapv_inplace
8. Funkce mapv_into: kombinace mapv a mapv_inplace
9. Použití funkce vyššího řádu fold (reduce)
10. Makro azip! pro manipulaci s prvky většího množství polí
12. Větší množství producentů při použití struktury Zip
13. Repositář s demonstračními příklady
1. Programovací jazyk Rust: knihovna ndarray pro práci s n-rozměrnými poli (dokončení)
Již ve třetí části tohoto seriálu jsme se seznámili s funkcemi použitelnými při zpracování sekvencí vytvářených pomocí iterátorů. Tyto funkce mají jména i sémantiku převzatou z funkcionálních jazyků (poprvé se objevily v LISPu, který sice není čistě funkcionální, ale nalezneme v něm mnoho prvků, které se do FP jazyků posléze dostaly). Mezi zmíněné funkce – které by byly ve funkcionálních jazycích implementovány funkcemi vyššího řádu – patří zejména map, filter, take, take_while a fold (někde se nazývá reduce).
Rustovské funkce vyššího řádu ze standardní knihovny jsou určeny pro zpracování sekvencí, přičemž výsledkem jejich aplikace může být jiná sekvence s obecně odlišným počtem prvků. Typickým příkladem je zejména funkce filter vracející odlišnou sekvenci (v extrémním případě i sekvenci prázdnou). Některé funkce vyššího řádu nalezneme i v knihovně ndarray, ovšem v tomto případě se pochopitelně bude jednat o funkce, které na vstupu očekávají pole (jednorozměrný vektor, dvourozměrnou matici, vícerozměrné pole…) a výsledkem může být opět pole popř. jediná hodnota libovolného typu. Mezi funkce vyššího řádu, které nalezneme v knihovně ndarray, patří především:
Funkce/makro | Význam | Popsána v kapitole |
---|---|---|
map | postupná aplikace funkce na každý prvek, výsledkem je nové pole | 2 |
mapv | postupná aplikace funkce na každý prvek předaný hodnotou | 3 |
mapv_inplace | dtto, ale původní pole se změní (nevytvoří se nové) | 7 |
mapv_into | kombinace předchozích dvou funkcí | 8 |
fold | postupná „redukce“ prvků v poli s akumulací výsledku | 9 |
azip! | makro umožňující například kombinaci prvků dvou polí | 10 |
2. Funkce vyššího řádu map a jednorozměrná pole
Základní funkcí vyššího řádu, kterou v knihovně ndarray nalezneme, je funkce nazvaná map. Ta postupně aplikuje jí předanou funkci na jednotlivé prvky nějakého pole (nezávisle na počtu dimenzí) a vytváří tak pole nové. Tento čistě funkcionální přístup, kdy se původní pole nemění, je sice velmi užitečný a z hlediska teorie „čistý“, ovšem lze ho aplikovat jen na relativně malá pole, nikoli například na rozsáhlé matice s miliony prvků. V takovém případě by se totiž zvyšovaly paměťové nároky, snižovala by se efektivnost cache atd.
Podívejme se nyní na způsob aplikace (anonymních) funkcí na prvky vektoru obsahujícího hodnoty 0 až 12. Povšimněte si, že se ve funkci map pracuje s referencí na prvky, což je výhodné, protože se neprovádí jejich klonování:
let vector1 = Array::from_iter(0..12); // vektor s prvky 0, 2, 4 ... let vector2 = vector1.map(|x| 2* *x); // vektor s prvky 0, 1, 0, 1, ... (1 pro sudé hodnoty) let vector3 = vector1.map(|x| *x % 2); // vektor s prvky true, false, true, false (test, zda je hodnota sudá) let vector4 = vector1.map(|x| *x % 2 == 0); println!("vector1: {}", vector1); println!("vector2: {}", vector2); println!("vector3: {}", vector3); println!("vector4: {}", vector4);
Výsledek běhu výše uvedeného úryvku kódu:
vector1: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] vector2: [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22] vector3: [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1] vector4: [true, false, true, false, true, false, true, false, true, false, true, false]
Poznámka: povšimněte si, že nově vytvořené pole může obsahovat prvky jiného typu než pole původní.
3. Předávání prvků hodnotou a referencí, automatická dereference prvků
Pokud chcete prvky předávat hodnotou a nikoli odkazem (referencí), použijte namísto funkce map funkci nazvanou mapv. V (typicky) anonymní funkci se bude pracovat přímo s hodnotami prvků, což mj. znamená, že může dojít k jejich klonování:
let vector1 = Array::from_iter(0..12); // vektor s prvky 0, 2, 4 ... let vector2 = vector1.mapv(|x| 2*x); // vektor s prvky 0, 1, 0, 1, ... (1 pro sudé hodnoty) let vector3 = vector1.mapv(|x| x % 2); // vektor s prvky true, false, true, false (test, zda je hodnota sudá) let vector4 = vector1.mapv(|x| x % 2 == 0); println!("vector1: {}", vector1); println!("vector2: {}", vector2); println!("vector3: {}", vector3); println!("vector4: {}", vector4);
Výsledek běhu výše uvedeného úryvku kódu:
vector1: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] vector2: [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22] vector3: [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1] vector4: [true, false, true, false, true, false, true, false, true, false, true, false]
Ve skutečnosti se díky automatickému použití traitu Deref může i v anonymní funkci předané do map pracovat přímo s hodnotami prvků:
let vector1 = Array::from_iter(0..12); // vektor s prvky 0, 2, 4 ... let vector2 = vector1.map(|x| 2*x); // vektor s prvky 0, 1, 0, 1, ... (1 pro sudé hodnoty) let vector3 = vector1.map(|x| x % 2); // vektor s prvky true, false, true, false (test, zda je hodnota sudá) let vector4 = vector1.map(|x| x % 2 == 0); println!("vector1: {}", vector1); println!("vector2: {}", vector2); println!("vector3: {}", vector3); println!("vector4: {}", vector4);
Výsledek běhu výše uvedeného úryvku kódu je shodný s předchozím výsledkem:
vector1: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] vector2: [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22] vector3: [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1] vector4: [true, false, true, false, true, false, true, false, true, false, true, false]
4. Funkce vyššího řádu map a dvourozměrná i trojrozměrná pole
Funkci vyššího řádu map samozřejmě nemusíme používat pouze pro jednorozměrné vektory, ale lze ji zavolat i pro dvourozměrné matice či pro vícerozměrná pole. Podívejme se na aplikaci různých anonymních funkcí na prvky matice 3×4 prvky:
let array1 = Array::from_iter(0..12).into_shape((3,4)).unwrap(); // vektor s prvky 0, 2, 4 ... let array2 = array1.map(|x| 2* *x); // vektor s prvky 0, 1, 0, 1, ... (1 pro sudé hodnoty) let array3 = array1.map(|x| *x % 2); // vektor s prvky true, false, true, false (test, zda je hodnota sudá) let array4 = array1.map(|x| *x % 2 == 0); println!("array1:\n{}\n", array1); println!("array2:\n{}\n", array2); println!("array3:\n{}\n", array3); println!("array4:\n{}\n", array4);
Výsledek běhu výše uvedeného úryvku kódu:
array1: [[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]] array2: [[0, 2, 4, 6], [8, 10, 12, 14], [16, 18, 20, 22]] array3: [[0, 1, 0, 1], [0, 1, 0, 1], [0, 1, 0, 1]] array4: [[true, false, true, false], [true, false, true, false], [true, false, true, false]]
Aplikace na trojrozměrné pole o rozměrech 3×3×3 prvky:
let array1 = Array::from_iter(0..27).into_shape((3,3,3)).unwrap(); // vektor s prvky 0, 2, 4 ... let array2 = array1.map(|x| 2* *x); // vektor s prvky 0, 1, 0, 1, ... (1 pro sudé hodnoty) let array3 = array1.map(|x| *x % 2); // vektor s prvky true, false, true, false (test, zda je hodnota sudá) let array4 = array1.map(|x| *x % 2 == 0); println!("array1:\n{}\n", array1); println!("array2:\n{}\n", array2); println!("array3:\n{}\n", array3); println!("array4:\n{}\n", array4);
Výsledek běhu výše uvedeného úryvku kódu bude nyní následující:
array1: [[[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]]] array2: [[[0, 2, 4], [6, 8, 10], [12, 14, 16]], [[18, 20, 22], [24, 26, 28], [30, 32, 34]], [[36, 38, 40], [42, 44, 46], [48, 50, 52]]] array3: [[[0, 1, 0], [1, 0, 1], [0, 1, 0]], [[1, 0, 1], [0, 1, 0], [1, 0, 1]], [[0, 1, 0], [1, 0, 1], [0, 1, 0]]] array4: [[[true, false, true], [false, true, false], [true, false, true]], [[false, true, false], [true, false, true], [false, true, false]], [[true, false, true], [false, true, false], [true, false, true]]]
5. Složitější příklad: vektory a pole obsahující komplexní čísla
Ukažme si nyní nepatrně složitější příklad, v němž budeme používat vektory a pole obsahující komplexní čísla. Celý příklad je rozdělen do několika částí. První část specifikuje, které moduly (a jejich jmenné prostory) budeme využívat:
extern crate ndarray; use ndarray::Array; use std::fmt;
Dále deklarujeme strukturu reprezentující komplexní čísla. Její nejjednodušší varianta (bez použití generických typů) bude následující:
struct Complex { real: f32, imag: f32, }
Pro komplexní čísla dále implementujeme konstruktor new a funkci pro výpočet absolutní hodnoty abs. Právě tato funkce je v kontextu tohoto příkladu důležitá:
impl Complex { fn new(real: f32, imag: f32) -> Complex { Complex{real:real, imag:imag} } fn abs(&self) -> f32 { (self.real * self.real + self.imag * self.imag).sqrt() } }
Pro snazší výpis vektorů a polí komplexních čísel navíc implementujeme i funkci fmt, která bude automaticky volána v makru println! (funkce fmt má podobný význam jako metoda toString v Javě, jen je ji možné použít i pro zápis do souborů atd.):
impl fmt::Debug for Complex { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}+{}i", self.real, self.imag) } }
6. Funkce vyššího řádu map a pole komplexních čísel
Další funkce definovaná v demonstračním příkladu nejdříve vytvoří jednorozměrný vektor obsahující pětici komplexních čísel a následně aplikuje funkci/metodu Complex::abs na každý prvek tohoto vektoru. Výsledkem bude opět vektor, tentokrát však jeho prvky budou mít typ f32 a nikoli Complex!. Ostatně se o tom snadno přesvědčíme na výstupu:
fn map_complex() { println!("map_complex"); let vector1 = Array::from_vec(vec![Complex::new(0.0, 0.0), Complex::new(1.0, 0.0), Complex::new(0.0, 1.0), Complex::new(1.0, 1.0), Complex::new(100.0, 100.0)]); let vector2 = vector1.map(|x| Complex::abs(x)); println!("vector1: {:?}", vector1); println!("vector2: {:?}", vector2); println!() }
Prakticky stejným způsobem můžeme vytvořit matici komplexních čísel o rozměrech 3×3 prvky. Po aplikaci funkce/metody Complex::abs na jednotlivé prvky této matice získáme matici novou, tentokrát však budou prvky této matice 3×3 prvky typu f32:
fn map_complex_2d() { println!("map_complex_2d"); let array1 = Array::from_shape_vec((3,3), vec![Complex::new(0.0, 0.0), Complex::new(1.0, 0.0), Complex::new(2.0, 0.0), Complex::new(0.0, 1.0), Complex::new(1.0, 1.0), Complex::new(2.0, 1.0), Complex::new(0.0, 2.0), Complex::new(1.0, 2.0), Complex::new(2.0, 2.0)]).unwrap(); let array2 = array1.map(|x| Complex::abs(x)); println!("vector1:\n{:?}\n", array1); println!("vector2:\n{:?}\n", array2); println!() }
Nyní nám jen zbývá obě výše uvedené funkce zavolat:
fn main() { map_complex(); map_complex_2d(); }
Po překladu a spuštění tohoto příkladu by se měly na standardní výstup vypsat následující řádky:
map_complex vector1: [0+0i, 1+0i, 0+1i, 1+1i, 100+100i] shape=[5], strides=[1], layout=C | F (0x3) vector2: [0, 1, 1, 1.4142135, 141.42136] shape=[5], strides=[1], layout=C | F (0x3) map_complex_2d vector1: [[0+0i, 1+0i, 2+0i], [0+1i, 1+1i, 2+1i], [0+2i, 1+2i, 2+2i]] shape=[3, 3], strides=[3, 1], layout=C (0x1) vector2: [[0, 1, 2], [1, 1.4142135, 2.236068], [2, 2.236068, 2.828427]] shape=[3, 3], strides=[3, 1], layout=C (0x1)
7. Přímá modifikace prvků pole pomocí funkce mapv_inplace
Jak jsme si již vysvětlili u popisu funkce map, může být čistě funkcionální přístup při zpracování rozsáhlých polí problematický, protože vede k neustálému vytváření polí nových. Jedno z řešení spočívá v aplikaci nějaké zvolené funkce na prvky pole, ovšem s tím rozdílem, že se původní prvek přepíše výslednou hodnotou dané funkce. Celé zpracování je tedy skutečně provedeno „in situ“ se všemi výhodami (žádná další spotřeba paměti) i nevýhodami (měníme stav objektu, což je obecně postup, kterému se snažíme vyhnout).
Vzhledem k tomu, že se prvky pole modifikují, musí být celé pole měnitelné (mutable):
let mut vector1 = Array::from_iter(0..12); println!("vector1: {}", vector1);
Na takové pole (zde konkrétně jednorozměrný vektor) můžeme volat funkci mapv_inplace. Povšimněte si, jak lze použít rozhodovací konstrukci if-then-else:
vector1.mapv_inplace(|x| x*2); println!("vector1: {}", vector1); vector1.mapv_inplace(|x| if x<10 {0} else {1}); println!("vector1: {}", vector1);
Výsledek běhu výše uvedeného úryvku kódu:
vector1: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] vector1: [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22] vector1: [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1]
Podobně budeme postupovat u dvourozměrných polí:
let mut array1 = Array::from_iter(0..12).into_shape((3,4)).unwrap(); println!("array1:\n{}\n", array1); array1.mapv_inplace(|x| if x%2 == 0 {0} else {1}); println!("array1:\n{}\n", array1);
Výsledek běhu výše uvedeného úryvku kódu:
array1: [[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]] array1: [[0, 1, 0, 1], [0, 1, 0, 1], [0, 1, 0, 1]]
Aplikace na trojrozměrné pole o rozměrech 3×3×3 prvky:
let mut array1 = Array::from_iter(0..27).into_shape((3,3,3)).unwrap(); println!("array1:\n{}\n", array1); array1.mapv_inplace(|x| if x%2 == 0 {0} else {1}); println!("array1:\n{}\n", array1);
Výsledek běhu výše uvedeného úryvku kódu:
array1: [[[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]]] array1: [[[0, 1, 0], [1, 0, 1], [0, 1, 0]], [[1, 0, 1], [0, 1, 0], [1, 0, 1]], [[0, 1, 0], [1, 0, 1], [0, 1, 0]]]
8. Funkce mapv_into: kombinace mapv a mapv_inplace
Kromě již popsaných funkcí vyššího řádu map, mapv a mapv_inplace existuje ještě funkce nazvaná mapv_into, která přebírá vlastnosti obou předchozích variant mapv*. Funkce mapv_into totiž „konzumuje“ původní pole a vrací pole nové. Ve skutečnosti ovšem opět pracuje „in situ“, takže je její použití paměťově efektivní, ovšem navíc se tato funkce chová více funkcionálně, protože po konzumaci původního pole ho již není možné použít a musíme namísto toho použít návratovou hodnotu mapv_into (toto chování si zkontroluje překladač):
let vector1 = Array::from_iter(0..12); let vector2 = vector1.mapv_into(|x| x*2); let vector3 = vector2.mapv_into(|x| if x<10 {0} else {1}); println!("vector3: {}", vector3);
Výsledek běhu výše uvedeného úryvku kódu:
vector3: [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1]
9. Použití funkce vyššího řádu fold (reduce)
Další velmi užitečnou funkcí vyššího řádu, která nesmí chybět v repertoáru žádné funkcionálně zaměřené knihovny (a tím pádem ani v repertoáru „funkcionální“ části knihovny ndarray), je funkce pojmenovaná reduce popř. fold. Názvy těchto funkcí naznačují jejich účel – dochází k postupné „redukci“ prvků uložených v poli, a to (postupnou) aplikací zvolené uživatelské funkce na jednotlivé prvky a po krocích počítaný mezivýsledek (někdy se podle svého chování nazývá akumulátor).
V knihovně ndarray se příslušná metoda, která toto chování pro libovolné pole zajišťuje, jmenuje fold a akceptuje dva parametry – počáteční hodnotu akumulátoru (mezivýsledku) a uzávěr, což je zjednodušeně řečeno funkce (typicky anonymní), která na sebe má navázánu alespoň jednu volnou proměnnou. Tento uzávěr je postupně volán se dvěma argumenty, tedy s aktuální hodnotou akumulátoru (mezivýsledku) a prvkem získaným ze vstupní sekvence. Akumulátor je po každém volání modifikován tak, že obsahuje hodnotou vrácenou uzávěrem.
Podívejme se na příklad, v němž sečteme všechny prvky jednorozměrného pole a posléze vypočteme součin všech prvků. Při výpočtu součtu je počáteční hodnota akumulátoru nulová, u součinu musí být samozřejmě nastavena na jedničku:
let vector1 = Array::from_iter(1..11); let sum = vector1.fold(0, |acc, value| acc+value); let product = vector1.fold(1, |acc, value| acc*value); println!("vector1: {}", vector1); println!("sum: {}", sum); println!("product: {}", product);
Výsledek běhu výše uvedeného úryvku kódu:
vector1: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] sum: 55 product: 3628800
Ve složitějším příkladu provedeme stejnou operaci, ovšem s maticí 3×3 prvky. Navíc ještě vypočteme součet prvků s počáteční hodnotou akumulátoru nastavenou na 1000:
let array = Array::from_shape_vec((3,3), vec![1,2,3,4,5,6,7,8,9]).unwrap(); let sum = array.fold(0, |acc, value| acc+value); let sum_offset = array.fold(1000, |acc, value| acc+value); let product = array.fold(1, |acc, value| acc*value); println!("array:\n{}\n", array); println!("sum: {}", sum); println!("sum_offset: {}", sum_offset); println!("product: {}", product);
Výsledek běhu výše uvedeného úryvku kódu:
array: [[1, 2, 3], [4, 5, 6], [7, 8, 9]] sum: 45 sum_offset: 1045 product: 362880
10. Makro azip! pro manipulaci s prvky většího množství polí
Minule jsme si mj. popsali i operátory, které byly přetížené takovým způsobem, aby je bylo možné využít pro součet, rozdíl, součin či podíl korespondujících prvků dvou polí. Ve skutečnosti je ovšem použití přetížených operátorů poměrně omezené, což ale nemusí představovat větší problém, a to díky existenci makra nazvaného azip!. Toto makro dokáže zpracovat větší množství vstupních polí, přičemž aplikuje zvolený výraz na každou kombinaci korespondujících prvků vstupních polí. Nejlepší bude, když si ukážeme jednoduchý příklad pro součet dvou vektorů.
Mějme dva vstupní vektory:
let vector1 = Array::from_iter(1..11); let vector2 = Array::from_iter(1..11);
A vektor pro uložení výsledku. Tento vektor musí mít shodnou velikost a musí být samozřejmě měnitelný (mutable):
let mut result = Array::zeros((10));
Nyní korespondující prvky vektoru sečteme použitím makra azip!. Před slovem in se specifikuje výsledné pole i pole vstupní. Výraz ve složených závorkách se aplikuje na korespondující prvky vektorů:
azip!(mut result, vector1, vector2 in { *result = vector1 + vector2 }); println!("vector1: {}", vector1); println!("vector2: {}", vector2); println!("result sum: {}", result);
Původní vstupní vektory nejsou „zkonzumovány“, takže je lze použít v dalším výpočtu:
let mut result_mul = Array::zeros((10)); azip!(mut result_mul, vector1, vector2 in { *result_mul = vector1 * vector2 }); println!("result_mul: {}", result_mul);
Výsledek běhu výše uvedeného úryvku kódu:
vector1: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] vector2: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] result sum: [2, 4, 6, 8, 10, 12, 14, 16, 18, 20] result_mul: [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
11. Použití struktury Zip
Ve skutečnosti se makro azip! expanduje na složitější výraz se strukturou Zip. Tato struktura poskytuje (přesněji řečeno implementuje) několik funkcí umožňujících specifikovat výstupní pole, vstupní (zdrojová) pole i prováděnou operaci. Tato operace se typicky zadává formou anonymní funkce předané do funkce vyššího řádu apply. Opět se podívejme na příklad podobný příkladu z předchozí kapitoly.
Mějme dva vstupní vektory:
let vector1 = Array::from_iter(1..11); let vector2 = Array::from_iter(1..11);
A vektor pro uložení výsledku. Tento vektor musí mít shodnou velikost a musí být samozřejmě měnitelný (mutable):
let mut result = Array::zeros((10));
Nyní s využitím metod from a and specifikujeme vstupní a výstupní pole a následně přes funkci vyššího řádu apply uvedeme, jaká operace se má provádět s korespondujícími prvky vstupních polí. Ty se předávají přes referenci:
Zip::from(&mut result) .and(&vector1) .and(&vector2) .apply(|result, &vector1, &vector2| { *result = vector1 + vector2 }); println!("vector1: {}", vector1); println!("vector2: {}", vector2); println!("result sum: {}", result);
Výsledek běhu výše uvedeného úryvku kódu:
vector1: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] vector2: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] result sum: [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
12. Větší množství producentů při použití struktury Zip
Ve skutečnosti je možné specifikovat větší (prakticky libovolné) množství vstupních polí, což je patrné z následujícího příkladu se čtyřmi vstupními poli:
let vector1 = Array::from_iter(1..11); let vector2 = Array::from_iter(1..11); let vector3 = Array::from_vec(vec![1,2,3,1,2,3,1,2,3,1000]); let vector4 = Array::from_vec(vec![1,1,1,2,2,2,3,3,3,4]); let mut result = Array::zeros((10)); Zip::from(&mut result) .and(&vector1) .and(&vector2) .and(&vector3) .and(&vector4) .apply(|result, &vector1, &vector2, &vector3, &vector4| { *result = vector1 + vector2 * vector3 / vector4 }); println!("vector1: {}", vector1); println!("vector2: {}", vector2); println!("vector3: {}", vector3); println!("vector4: {}", vector4); println!("result sum: {}", result);
Výsledek běhu výše uvedeného úryvku kódu:
vector1: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] vector2: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] vector3: [1, 2, 3, 1, 2, 3, 1, 2, 3, 1000] vector4: [1, 1, 1, 2, 2, 2, 3, 3, 3, 4] result sum: [2, 6, 12, 6, 10, 15, 9, 13, 18, 2510]
Poznámka: zápis je poměrně složitý, což je i jeden z důvodů, proč je mnohem jednodušší používat makro azip!.
13. Repositář s demonstračními příklady
Všechny dnes popisované demonstrační příklady (Rustovské projekty) byly, ostatně podobně jako ve všech předchozích částech tohoto seriálu, uloženy do Git repositáře dostupného na adrese https://github.com/tisnik/presentations. Demonstrační příklady si můžete v případě potřeby stáhnout i jednotlivě bez nutnosti klonovat celý repositář (ovšem u projektů je lepší mít celý repositář, abyste nemuseli pracně stahovat všechny potřebné soubory):
Každý projekt se přeloží příkazem:
cargo build
Jeho následné spuštění zajistí příkaz:
cargo clean
14. Odkazy na Internetu
- Map (higher-order function)
https://en.wikipedia.org/wiki/Map_%28higher-order_function%29 - Fold (higher-order function)
https://en.wikipedia.org/wiki/Fold_%28higher-order_function%29 - ndarray – dokumentace k modulu
https://bluss.github.io/rust-ndarray/master/ndarray/index.html - ndarray – Crate
https://crates.io/crates/ndarray - rustup
https://www.rustup.rs/ - rustup: the Rust toolchain installer (Git repositář + dokumentace)
https://github.com/rust-lang-nursery/rustup.rs - The Rust FFI Omnibus
http://jakegoulding.com/rust-ffi-omnibus/ - Build Script Support
http://doc.crates.io/build-script.html - Calling Rust From Python
https://bheisler.github.io/post/calling-rust-in-python/ - Calling Rust in Python (komentáře k předchozímu článku)
https://www.reddit.com/r/rust/comments/63iy5a/calling_rust_in_python/ - CFFI Documentation
https://cffi.readthedocs.io/en/latest/ - Build Script Support
http://doc.crates.io/build-script.html - Creating a shared and static library with the gnu compiler [gcc]
http://www.adp-gmbh.ch/cpp/gcc/create_lib.html - ctypes — A foreign function library for Python
https://docs.python.org/2/library/ctypes.html - FFI: Foreign Function Interface
https://doc.rust-lang.org/book/ffi.html - Primitive Type pointer
https://doc.rust-lang.org/std/primitive.pointer.html - Cargo: správce projektů a balíčků pro programovací jazyk Rust
https://mojefedora.cz/cargo-spravce-projektu-a-balicku-pro-programovaci-jazyk-rust/ - Network Communication and Serialization in Rust
https://www.safaribooksonline.com/blog/2014/01/28/network-communication-serialization-rust/ - Crate bincode
http://tyoverby.com/bincode/bincode/index.html - Struct std::fs::File
https://doc.rust-lang.org/std/fs/struct.File.html - Trait std::io::Seek
https://doc.rust-lang.org/std/io/trait.Seek.html - Trait std::io::Read
https://doc.rust-lang.org/std/io/trait.Read.html - Trait std::io::Write
https://doc.rust-lang.org/std/io/trait.Write.html - Trait std::io::BufRead
https://doc.rust-lang.org/std/io/trait.BufRead.html - Module std::io::prelude
https://doc.rust-lang.org/std/io/prelude/index.html - std::net::IpAddr
https://doc.rust-lang.org/std/net/enum.IpAddr.html - std::net::Ipv4Addr
https://doc.rust-lang.org/std/net/struct.Ipv4Addr.html - std::net::Ipv6Addr
https://doc.rust-lang.org/std/net/struct.Ipv6Addr.html - TcpListener
https://doc.rust-lang.org/std/net/struct.TcpListener.html - TcpStream
https://doc.rust-lang.org/std/net/struct.TcpStream.html - Binary heap (Wikipedia)
https://en.wikipedia.org/wiki/Binary_heap - Binární halda (Wikipedia)
https://cs.wikipedia.org/wiki/Bin%C3%A1rn%C3%AD_halda - Halda (datová struktura)
https://cs.wikipedia.org/wiki/Halda_%28datov%C3%A1_struktura%29 - Struct std::collections::HashSet
https://doc.rust-lang.org/std/collections/struct.HashSet.html - Struct std::collections::BTreeSet
https://doc.rust-lang.org/std/collections/struct.BTreeSet.html - Struct std::collections::BinaryHeap
https://doc.rust-lang.org/std/collections/struct.BinaryHeap.html - Set (abstract data type)
https://en.wikipedia.org/wiki/Set_%28abstract_data_type%29#Language_support - Associative array
https://en.wikipedia.org/wiki/Associative_array - Hash Table
https://en.wikipedia.org/wiki/Hash_table - B-tree
https://en.wikipedia.org/wiki/B-tree - Pedro Celis: Robin Hood Hashing (naskenované PDF!)
https://cs.uwaterloo.ca/research/tr/1986/CS-86–14.pdf - Robin Hood hashing
http://codecapsule.com/2013/11/11/robin-hood-hashing/ - Robin Hood hashing: backward shift deletion
http://codecapsule.com/2013/11/17/robin-hood-hashing-backward-shift-deletion/ - Module std::collections
https://doc.rust-lang.org/std/collections/ - Module std::vec
https://doc.rust-lang.org/nightly/std/vec/index.html - Struct std::collections::VecDeque
https://doc.rust-lang.org/std/collections/struct.VecDeque.html - Struct std::collections::LinkedList
https://doc.rust-lang.org/std/collections/struct.LinkedList.html - Module std::fmt
https://doc.rust-lang.org/std/fmt/ - Macro std::println
https://doc.rust-lang.org/std/macro.println.html - Enum std::result::Result
https://doc.rust-lang.org/std/result/enum.Result.html - Module std::result
https://doc.rust-lang.org/std/result/ - Result
http://rustbyexample.com/std/result.html - Rust stdlib: Option
https://doc.rust-lang.org/std/option/enum.Option.html - Module std::option
https://doc.rust-lang.org/std/option/index.html - Rust by example: option
http://rustbyexample.com/std/option.html - Rust by example: if-let
http://rustbyexample.com/flow_control/if_let.html - Rust by example: while let
http://rustbyexample.com/flow_control/while_let.html - Rust by example: Option<i32>
http://rustbyexample.com/std/option.html - An Overview of Macros in Rust
http://words.steveklabnik.com/an-overview-of-macros-in-rust - A Practical Intro to Macros in Rust 1.0
https://danielkeep.github.io/practical-intro-to-macros.html - The Rust Programming Language: macros
https://doc.rust-lang.org/beta/book/macros.html - Rust by example: 15 macro_rules!
http://rustbyexample.com/macros.html - Primitive Type isize
https://doc.rust-lang.org/nightly/std/primitive.isize.html - Primitive Type usize
https://doc.rust-lang.org/nightly/std/primitive.usize.html - Primitive Type array
https://doc.rust-lang.org/nightly/std/primitive.array.html - Module std::slice
https://doc.rust-lang.org/nightly/std/slice/ - Rust by Example: 2.3 Arrays and Slices
http://rustbyexample.com/primitives/array.html - What is the difference between Slice and Array (stackoverflow)
http://stackoverflow.com/questions/30794235/what-is-the-difference-between-slice-and-array - Learning Rust With Entirely Too Many Linked Lists
http://cglab.ca/~abeinges/blah/too-many-lists/book/ - Testcase: linked list
http://rustbyexample.com/custom_types/enum/testcase_linked_list.html - Operators and Overloading
https://doc.rust-lang.org/book/operators-and-overloading.html - Module std::ops
https://doc.rust-lang.org/std/ops/index.html - Module std::cmp
https://doc.rust-lang.org/std/cmp/index.html - Trait std::ops::Add
https://doc.rust-lang.org/stable/std/ops/trait.Add.html - Trait std::ops::AddAssign
https://doc.rust-lang.org/std/ops/trait.AddAssign.html - Trait std::ops::Drop
https://doc.rust-lang.org/std/ops/trait.Drop.html - Trait std::cmp::Eq
https://doc.rust-lang.org/std/cmp/trait.Eq.html - Struct std::boxed::Box
https://doc.rust-lang.org/std/boxed/struct.Box.html - Explore the ownership system in Rust
https://nercury.github.io/rust/guide/2015/01/19/ownership.html - Rust's ownership and move semantic
http://www.slideshare.net/saneyuki/rusts-ownership-and-move-semantics - Trait std::marker::Copy
https://doc.rust-lang.org/stable/std/marker/trait.Copy.html - Trait std::clone::Clone
https://doc.rust-lang.org/stable/std/clone/trait.Clone.html - The Stack and the Heap
https://doc.rust-lang.org/book/the-stack-and-the-heap.html - Rust Compare: Pointers & References
http://www.rust-compare.com/site/pointers.html - Rust Compare: Parameters
http://www.rust-compare.com/site/params.html - Why does this compile? Automatic dereferencing?
https://users.rust-lang.org/t/why-does-this-compile-automatic-dereferencing/2183 - Understanding Pointers, Ownership, and Lifetimes in Rust
http://koerbitz.me/posts/Understanding-Pointers-Ownership-and-Lifetimes-in-Rust.html - Rust lang series episode #25 — pointers (#rust-series)
https://steemit.com/rust-series/@jimmco/rust-lang-series-episode-25-pointers-rust-series - Rust – home page
https://www.rust-lang.org/en-US/ - Rust – Frequently Asked Questions
https://www.rust-lang.org/en-US/faq.html - Destructuring and Pattern Matching
https://pzol.github.io/getting_rusty/posts/20140417_destructuring_in_rust/ - The Rust Programming Language
https://doc.rust-lang.org/book/ - Rust (programming language)
https://en.wikipedia.org/wiki/Rust_%28programming_language%29 - Go – home page
https://golang.org/ - Stack Overflow – Most Loved, Dreaded, and Wanted language
https://stackoverflow.com/research/developer-survey-2016#technology-most-loved-dreaded-and-wanted - Rust vs Go (dva roky staré hodnocení, od té doby došlo k posunům v obou jazycích)
http://jaredforsyth.com/2014/03/22/rust-vs-go/ - Rust vs Go: My experience
https://www.reddit.com/r/golang/comments/21m6jq/rust_vs_go_my_experience/ - Friends of Rust (Organizations running Rust in production)
https://www.rust-lang.org/en-US/friends.html - Rust programs versus C++ g++
https://benchmarksgame.alioth.debian.org/u64q/compare.php?lang=rust&lang2=gpp - Další benchmarky (nejedná se o reálné příklady „ze života“)
https://github.com/kostya/benchmarks - Go na Redditu
https://www.reddit.com/r/golang/ - Rust vs. Go
http://vschart.com/compare/rust/vs/go-language - Abstraction without overhead: traits in Rust
https://blog.rust-lang.org/2015/05/11/traits.html - Method Syntax
https://doc.rust-lang.org/book/method-syntax.html - Traits in Rust
https://doc.rust-lang.org/book/traits.html - Functional Programming in Rust – Part 1 : Function Abstraction
http://blog.madhukaraphatak.com/functional-programming-in-rust-part-1/ - Of the emerging systems languages Rust, D, Go and Nim, which is the strongest language and why?
https://www.quora.com/Of-the-emerging-systems-languages-Rust-D-Go-and-Nim-which-is-the-strongest-language-and-why - Chytré ukazatele (moderní verze jazyka C++) [MSDN]
https://msdn.microsoft.com/cs-cz/library/hh279674.aspx - UTF-8 Everywhere
http://utf8everywhere.org/ - Rust by Example
http://rustbyexample.com/ - Rust oficiálně ve Fedoře
https://mojefedora.cz/rust-oficialne-ve-fedore/ - Resource acquisition is initialization
https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization - TIOBE index (October 2016)
http://www.tiobe.com/tiobe-index/ - Porovnání Go, D a Rustu na OpenHubu:
https://www.openhub.net/languages/compare?language_name[]=-1&language_name[]=-1&language_name[]=dmd&language_name[]=golang&language_name[]=rust&language_name[]=-1&measure=commits - String Types in Rust
http://www.suspectsemantics.com/blog/2016/03/27/string-types-in-rust/ - Trait (computer programming)
https://en.wikipedia.org/wiki/Trait_%28computer_programming%29 - Type inference
https://en.wikipedia.org/wiki/Type_inference