diff --git a/priprava/rust_priprava7.md b/priprava/rust_priprava7.md new file mode 100644 index 0000000..480abec --- /dev/null +++ b/priprava/rust_priprava7.md @@ -0,0 +1,540 @@ +# itertools — krok za krokom + +Každá kapitola: prečítaj, pozri príklady, urob úlohy. Až potom choď ďalej. + +```rust +use itertools::Itertools; // toto treba vždy +``` + +`Cargo.toml`: +```toml +[dependencies] +itertools = "0.14" +``` + +itertools pridáva metódy na každý iterátor. Nič špeciálne netreba robiť — len `use itertools::Itertools` a všetky metódy sú k dispozícii na `.iter()`, `.into_iter()` atď. + +--- + +## Kapitola 1: sorted — zoradenie iterátora + +Štandardný Rust nemá `.sorted()` na iterátoroch. itertools ho pridáva. + +```rust +use itertools::Itertools; + +let cisla = vec![3, 1, 4, 1, 5, 9, 2, 6]; + +// sorted() — zoradí vzostupne, vráti nový iterátor +let zoradene: Vec = cisla.iter().copied().sorted().collect(); +// [1, 1, 2, 3, 4, 5, 6, 9] + +// sorted_by — vlastné porovnanie +let zostupne: Vec = cisla.iter().copied() + .sorted_by(|a, b| b.cmp(a)) + .collect(); +// [9, 6, 5, 4, 3, 2, 1, 1] + +// sorted_by_key — zoraď podľa kľúča +let slova = vec!["banán", "auto", "citrón"]; +let podla_dlzky: Vec<&str> = slova.into_iter() + .sorted_by_key(|s| s.len()) + .collect(); +// ["auto", "banán", "citrón"] +``` + +So štruktúrami: +```rust +struct Student { meno: String, priemer: f32 } + +let studenti = vec![ + Student { meno: "Cyril".into(), priemer: 1.5 }, + Student { meno: "Anna".into(), priemer: 2.1 }, + Student { meno: "Boris".into(), priemer: 1.2 }, +]; + +// Zoraď podľa priemeru +let zoradeni: Vec<&Student> = studenti.iter() + .sorted_by(|a, b| a.priemer.partial_cmp(&b.priemer).unwrap()) + .collect(); +// Boris (1.2), Cyril (1.5), Anna (2.1) + +// Zoraď podľa mena abecedne +let abecedne: Vec<&Student> = studenti.iter() + .sorted_by_key(|s| &s.meno) + .collect(); +// Anna, Boris, Cyril +``` + +**Bez itertools** by si musel klonovať do Vec a volať `.sort()`: +```rust +// Bez itertools — zbytočne komplikované +let mut kopie = studenti.clone(); +kopie.sort_by(|a, b| a.priemer.partial_cmp(&b.priemer).unwrap()); + +// S itertools — jednoduché reťazenie +let vysledok: Vec<&Student> = studenti.iter() + .sorted_by(|a, b| a.priemer.partial_cmp(&b.priemer).unwrap()) + .collect(); +``` + +### Úlohy 1 + +**1a.** Máš vektor mien. Zoraď ich abecedne pomocou `.sorted()`. Vypíš. + +**1b.** Máš vektor čísel. Zoraď zostupne pomocou `.sorted_by()`. + +**1c.** Máš vektor reťazcov. Zoraď podľa dĺžky (najkratšie prvé) pomocou `.sorted_by_key()`. + +**1d.** Máš `Vec<(String, u32)>` — mená a veky. Zoraď podľa veku zostupne. + +--- + +## Kapitola 2: unique — odstránenie duplikátov + +```rust +use itertools::Itertools; + +let cisla = vec![1, 3, 2, 1, 4, 3, 2, 1, 5]; + +// unique() — odstráni duplikáty, zachová prvý výskyt +let unikatne: Vec<&i32> = cisla.iter().unique().collect(); +// [&1, &3, &2, &4, &5] — poradie prvého výskytu je zachované! + +// S owned hodnotami +let unikatne: Vec = cisla.into_iter().unique().collect(); +// [1, 3, 2, 4, 5] +``` + +Rozdiel oproti HashSet: `unique()` **zachováva poradie** prvého výskytu. HashSet nie. + +```rust +// unique_by — odstráni duplikáty podľa kľúča +struct Student { meno: String, trieda: String } + +let studenti = vec![ + Student { meno: "Anna".into(), trieda: "A".into() }, + Student { meno: "Boris".into(), trieda: "B".into() }, + Student { meno: "Cyril".into(), trieda: "A".into() }, + Student { meno: "Dana".into(), trieda: "B".into() }, +]; + +// Jeden študent z každej triedy (prvý nájdený) +let po_jednom: Vec<&Student> = studenti.iter() + .unique_by(|s| &s.trieda) + .collect(); +// Anna (trieda A), Boris (trieda B) — Cyril a Dana sú duplikáty tried +``` + +### Úlohy 2 + +**2a.** Máš vektor slov s duplikátmi. Odstráň duplikáty a zachovaj poradie. + +**2b.** Máš vektor `(String, i32)` — (meno, vek). Niektoré mená sa opakujú. Nechaj len prvý výskyt každého mena. Použi `unique_by`. + +**2c.** Máš vektor reťazcov. Chceš unikátne slová case-insensitive (t.j. "Ahoj" a "ahoj" sú duplikáty). Použi `unique_by` s `.to_lowercase()`. + +--- + +## Kapitola 3: join — spojenie do reťazca + +```rust +use itertools::Itertools; + +let slova = vec!["ahoj", "svet", "rust"]; + +// join — spojí prvky oddeľovačom +let veta: String = slova.iter().join(", "); +// "ahoj, svet, rust" + +let cisla = vec![1, 2, 3, 4, 5]; +let text: String = cisla.iter().join(" - "); +// "1 - 2 - 3 - 4 - 5" + +// Prázdny oddeľovač +let spolu: String = slova.iter().join(""); +// "ahojsvetrust" + +// Funguje s čímkoľvek čo implementuje Display +let floaty = vec![1.5, 2.7, 3.14]; +let text: String = floaty.iter().join(", "); +// "1.5, 2.7, 3.14" +``` + +**Bez itertools** by si to robil cez `.collect::>().join(",")` alebo ručne. + +### Úlohy 3 + +**3a.** Máš vektor mien. Spoj ich čiarkou a medzerou do jedného reťazca. + +**3b.** Máš vektor čísel 1 až 10. Spoj ich pomlčkou. + +**3c.** Máš vektor štruktúr `Kniha { nazov: String }`. Spoj názvy kníh do jedného reťazca oddeleného " | ". Hint: najprv `.map()` na názvy, potom `.join()`. + +--- + +## Kapitola 4: counts — počítanie výskytov + +```rust +use itertools::Itertools; + +let slova = vec!["ahoj", "svet", "ahoj", "rust", "svet", "ahoj"]; + +// counts() — vráti HashMap s počtom výskytov +let pocty = slova.iter().counts(); +// {&"ahoj": 3, &"svet": 2, &"rust": 1} + +// counts_by — počítaj podľa kľúča +let text = "Hello World"; +let pocty = text.chars().counts_by(|c| c.is_uppercase()); +// {true: 2, false: 9} (H a W sú veľké) +``` + +Toto nahrádza celý ten entry().or_insert(0) vzor: +```rust +// BEZ itertools — 4 riadky +let mut pocty = HashMap::new(); +for slovo in &slova { + *pocty.entry(slovo).or_insert(0) += 1; +} + +// S itertools — 1 riadok +let pocty = slova.iter().counts(); +``` + +### Úlohy 4 + +**4a.** Máš reťazec. Spočítaj výskyt každého znaku. Použi `.chars().counts()`. + +**4b.** Máš vektor štruktúr `Kniha { nazov: String, zaner: String }`. Spočítaj koľko kníh je v každom žánri. Použi `.map()` + `.counts()`. + +**4c.** Máš vektor čísel. Spočítaj koľko je párnych a koľko nepárnych. Použi `.counts_by()`. + +--- + +## Kapitola 5: group_by — zoskupovanie + +**Dôležité:** `group_by` funguje len na **už zoradených** dátach! Zoskupí po sebe idúce prvky s rovnakým kľúčom. + +```rust +use itertools::Itertools; + +// Dáta MUSIA byť zoradené podľa rovnakého kľúča! +let cisla = vec![1, 1, 2, 2, 2, 3, 3, 1]; + +// group_by zoskupí PO SEBE IDÚCE prvky +for (kluc, skupina) in &cisla.iter().group_by(|&&c| c) { + let prvky: Vec<&&i32> = skupina.collect(); + println!("{}: {:?}", kluc, prvky); +} +// 1: [&&1, &&1] +// 2: [&&2, &&2, &&2] +// 3: [&&3, &&3] +// 1: [&&1] ← POZOR! Posledná 1 je v NOVEJ skupine! +``` + +Ak chceš všetky rovnaké spolu, musíš najprv zoradiť: +```rust +let cisla = vec![1, 3, 2, 1, 2, 3, 1]; + +// Najprv zoradiť, potom group_by +for (kluc, skupina) in &cisla.iter().sorted().group_by(|&&c| c) { + let pocet = skupina.count(); + println!("{}: {}x", kluc, pocet); +} +// 1: 3x +// 2: 2x +// 3: 2x +``` + +So štruktúrami: +```rust +struct Kniha { nazov: String, zaner: String } + +let knihy = vec![/* ... */]; + +// Zoskup podľa žánru — musíš najprv zoradiť! +let zoradene: Vec<&Kniha> = knihy.iter() + .sorted_by_key(|k| &k.zaner) + .collect(); + +for (zaner, skupina) in &zoradene.iter().group_by(|k| &k.zaner) { + let knihy_v_zanri: Vec<&&&Kniha> = skupina.collect(); + println!("{}: {} kníh", zaner, knihy_v_zanri.len()); +} +``` + +**Tip:** Pre jednoduché počítanie použi radšej `.counts()` z kapitoly 4. `group_by` je užitočný keď chceš s celou skupinou niečo robiť (nie len počítať). + +### Úlohy 5 + +**5a.** Máš vektor slov zoradený abecedne. Zoskup podľa prvého písmena a vypíš koľko slov začína na ktoré písmeno. + +**5b.** Máš vektor čísel. Zoraď ich a potom zoskup rovnaké čísla. Pre každú skupinu vypíš číslo a koľkokrát sa vyskytuje. + +--- + +## Kapitola 6: chunk_by a chunks + +### chunks — rozdeľ na kúsky pevnej veľkosti + +```rust +use itertools::Itertools; + +let cisla = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + +// chunks(3) — skupiny po 3 +for chunk in &cisla.iter().chunks(3) { + let skupina: Vec<&i32> = chunk.collect(); + println!("{:?}", skupina); +} +// [1, 2, 3] +// [4, 5, 6] +// [7, 8, 9] +// [10] ← posledná skupina môže byť menšia +``` + +### tuple_windows — posuvné okno + +```rust +let cisla = vec![1, 2, 3, 4, 5]; + +// Dvojice po sebe idúcich +for (a, b) in cisla.iter().tuple_windows() { + println!("{} a {}", a, b); +} +// 1 a 2 +// 2 a 3 +// 3 a 4 +// 4 a 5 + +// Trojice +for (a, b, c) in cisla.iter().tuple_windows() { + println!("{}, {}, {}", a, b, c); +} +// 1, 2, 3 +// 2, 3, 4 +// 3, 4, 5 +``` + +### Úlohy 6 + +**6a.** Máš vektor 20 študentov. Rozdeľ ich do skupín po 5. Vypíš každú skupinu. + +**6b.** Máš vektor denných teplôt. Vypočítaj priemernú teplotu za každý 7-dňový úsek (chunks(7)). + +**6c.** Máš vektor čísel. Použi `tuple_windows` na nájdenie všetkých párov po sebe idúcich čísel, kde druhé je väčšie ako prvé (rastúce páry). + +--- + +## Kapitola 7: min_set, max_set — všetky minimá/maximá + +Štandardný `.min()` vráti len jedno minimum. Čo ak je ich viac? + +```rust +use itertools::Itertools; + +let cisla = vec![3, 1, 4, 1, 5, 1, 2]; + +// min_set — vráti Vec VŠETKÝCH miním +let minima: Vec<&i32> = cisla.iter().min_set(); +// [&1, &1, &1] — tri jednotky + +// max_set +let maxima: Vec<&i32> = cisla.iter().max_set(); +// [&5] + +// min_set_by_key — so štruktúrami +struct Student { meno: String, priemer: f32 } +let studenti = vec![/* ... */]; + +// Všetci študenti s najlepším priemerom +let najlepsi: Vec<&Student> = studenti.iter() + .min_set_by(|a, b| a.priemer.partial_cmp(&b.priemer).unwrap()); +``` + +### Úlohy 7 + +**7a.** Máš vektor čísel. Nájdi všetky výskyty maximálnej hodnoty. Koľko ich je? + +**7b.** Máš vektor slov. Nájdi všetky najdlhšie slová (môže ich byť viac rovnako dlhých). Použi `max_set_by_key`. + +--- + +## Kapitola 8: interleave a zip_eq + +### interleave — striedavo z dvoch iterátorov + +```rust +use itertools::Itertools; + +let a = vec![1, 3, 5]; +let b = vec![2, 4, 6]; + +let striedavo: Vec = a.into_iter().interleave(b.into_iter()).collect(); +// [1, 2, 3, 4, 5, 6] + +let x = vec!["a", "b", "c"]; +let y = vec!["1", "2", "3"]; +let mix: Vec<&str> = x.into_iter().interleave(y.into_iter()).collect(); +// ["a", "1", "b", "2", "c", "3"] +``` + +### zip_eq — zip ktorý panicky ak rôzna dĺžka + +Štandardný `.zip()` ticho skráti na kratší. `zip_eq` ti povie ak majú rôznu dĺžku: + +```rust +let mena = vec!["Anna", "Boris", "Cyril"]; +let veky = vec![25, 30, 22]; + +// Štandardný zip — OK +let pary: Vec<_> = mena.iter().zip(veky.iter()).collect(); + +// zip_eq — rovnaké, ale panikne ak rôzna dĺžka +let pary: Vec<_> = mena.iter().zip_eq(veky.iter()).collect(); + +// let veky_kratsi = vec![25, 30]; +// mena.iter().zip_eq(veky_kratsi.iter()).collect(); // PANIC! +``` + +### Úlohy 8 + +**8a.** Máš vektor otázok a vektor odpovedí. Spáruj ich a vypíš "Otázka: ... → Odpoveď: ...". Použi `zip_eq` (chceš aby program spadol ak nesedia počty). + +**8b.** Máš dva vektory — párne a nepárne čísla. Prelož ich striedavo do jedného vektora. + +--- + +## Kapitola 9: format_with a positions + +### positions — indexy prvkov splňajúcich podmienku + +```rust +use itertools::Itertools; + +let cisla = vec![10, 25, 30, 5, 40, 15]; + +// Indexy prvkov > 20 +let indexy: Vec = cisla.iter().positions(|c| *c > 20).collect(); +// [1, 2, 4] (25 je na indexe 1, 30 na 2, 40 na 4) +``` + +Toto je jednoduchšie ako `.enumerate().filter().map()`: +```rust +// BEZ itertools — dlhé +let indexy: Vec = cisla.iter().enumerate() + .filter(|(_, c)| **c > 20) + .map(|(i, _)| i) + .collect(); + +// S itertools — krátke +let indexy: Vec = cisla.iter().positions(|c| *c > 20).collect(); +``` + +### Úlohy 9 + +**9a.** Máš vektor slov. Nájdi pozície (indexy) všetkých slov dlhších ako 5 znakov. + +**9b.** Máš vektor študentov s priemerom. Nájdi indexy študentov s priemerom pod 1.5. + +--- + +## Kapitola 10: Kombinácie na skúške + +### Vzor 1: Zoradený výpis štatistík + +```rust +use itertools::Itertools; + +struct Kniha { nazov: String, vydavatelstvo: String } + +fn vypis_statistiky(knihy: &[Kniha]) { + // counts + sorted — zoradený výpis počtov + let pocty = knihy.iter().map(|k| &k.vydavatelstvo).counts(); + for (vydavatelstvo, pocet) in pocty.iter().sorted_by_key(|(_, p)| std::cmp::Reverse(*p)) { + println!("{}: {}", vydavatelstvo, pocet); + } +} +``` + +### Vzor 2: Unikátne zoradené hodnoty + +```rust +fn unikatne_zanre_zoradene(knihy: &[Kniha]) -> Vec { + knihy.iter() + .map(|k| k.vydavatelstvo.clone()) + .unique() + .sorted() + .collect() +} +``` + +### Vzor 3: Top N + +```rust +fn top_3_studenti(studenti: &[Student]) -> Vec<&Student> { + studenti.iter() + .sorted_by(|a, b| a.priemer.partial_cmp(&b.priemer).unwrap()) + .take(3) + .collect() +} +``` + +### Vzor 4: Formátovaný výpis zoznamu + +```rust +fn vypis_mena(studenti: &[Student]) -> String { + studenti.iter().map(|s| &s.meno).join(", ") +} +// "Anna, Boris, Cyril" +``` + +### Vzor 5: Zoskupenie a štatistiky + +```rust +fn priemer_podla_triedy(studenti: &[Student]) { + for (trieda, skupina) in &studenti.iter() + .sorted_by_key(|s| &s.trieda) + .group_by(|s| &s.trieda) + { + let zoznam: Vec<&Student> = skupina.collect(); + let sucet: f32 = zoznam.iter().map(|s| s.priemer).sum(); + let priemer = sucet / zoznam.len() as f32; + println!("Trieda {}: priemer {:.2}", trieda, priemer); + } +} +``` + +### Úlohy 10 + +**10a.** Máš vektor `Zamestnanec { meno: String, oddelenie: String, plat: u32 }`. Napíš funkciu, ktorá: +1. Zoskupí zamestnancov podľa oddelenia +2. Pre každé oddelenie vypíše mená zoradené abecedne, spojené čiarkou +3. Oddelenia vypíše zoradené abecedne + +Použi: `sorted_by_key`, `group_by`, `join`. + +**10b.** Máš vektor kníh. Nájdi 5 najčastejších žánrov. Použi `counts` + `sorted_by_key` + `take`. + +**10c.** Máš vektor čísel. Použi `tuple_windows` na zistenie najväčšieho rozdielu medzi dvoma po sebe idúcimi číslami. + +**10d.** Máš vektor reťazcov s duplikátmi. Vyfiltruj unikátne, zoraď abecedne, spoj do jedného reťazca oddeleného " | ". Všetko jedným reťazením. + +--- + +## Prehľad: kedy čo z itertools + +| Chcem... | Metóda | +|----------|--------| +| Zoradiť iterátor | `.sorted()`, `.sorted_by()`, `.sorted_by_key()` | +| Odstrániť duplikáty (so zachovaním poradia) | `.unique()`, `.unique_by()` | +| Spojiť do reťazca | `.join(separator)` | +| Spočítať výskyty → HashMap | `.counts()`, `.counts_by()` | +| Zoskupiť po sebe idúce | `.group_by()` (najprv sorted!) | +| Rozdeliť na kúsky | `.chunks(n)` | +| Posuvné okno | `.tuple_windows()` | +| Všetky minimá/maximá | `.min_set()`, `.max_set()` | +| Preložiť dva iterátory | `.interleave()` | +| Zip s kontrolou dĺžky | `.zip_eq()` | +| Indexy splňajúce podmienku | `.positions()` |