From 4b452112e99728f02e290419a1a6e128f50f67f9 Mon Sep 17 00:00:00 2001 From: Priec Date: Mon, 16 Feb 2026 19:50:35 +0100 Subject: [PATCH] prekopana priprava 2 --- priprava/rust_priprava3.md | 1119 ++++++++++++++++++++++++++++++++++++ 1 file changed, 1119 insertions(+) create mode 100644 priprava/rust_priprava3.md diff --git a/priprava/rust_priprava3.md b/priprava/rust_priprava3.md new file mode 100644 index 0000000..15cea1f --- /dev/null +++ b/priprava/rust_priprava3.md @@ -0,0 +1,1119 @@ +# Rust — Iterátory, Closures, Kombinácie (D–G) + +Pre každú tému: **vysvetlenie** → **príklady** → **úlohy na precvičenie** → riešenia na konci. + +--- + +## Časť D: Iterátory + +### D.1 — Čo je iterátor + +Iterátor je spôsob, ako prejsť cez kolekciu prvok po prvku. V Ruste existujú 3 spôsoby ako získať iterátor z kolekcie: + +| Metóda | Dáva ti | Kedy použiť | +|--------|---------|-------------| +| `.iter()` | `&T` (nemeniteľná referencia) | Chceš len čítať | +| `.iter_mut()` | `&mut T` (meniteľná referencia) | Chceš meniť prvky na mieste | +| `.into_iter()` | `T` (vlastníctvo) | Chceš konzumovať kolekciu | + +```rust +let mut cisla = vec![1, 2, 3]; + +// iter() — len čítam, cisla existujú ďalej +for c in cisla.iter() { + println!("{}", c); // c je &i32 +} + +// iter_mut() — mením prvky +for c in cisla.iter_mut() { + *c += 10; // c je &mut i32, treba dereferencovať +} +// cisla je teraz [11, 12, 13] + +// into_iter() — preberám vlastníctvo, cisla po tomto neexistujú +let cisla2 = vec![1, 2, 3]; +for c in cisla2.into_iter() { + println!("{}", c); // c je i32 +} +// cisla2 tu už nemôžeš použiť! +``` + +**Pravidlo:** Ak máš `&self` metódu (len čítaš), použi `.iter()`. Ak máš `&mut self`, použi `.iter_mut()`. + +--- + +### D.2 — filter + +**Čo robí:** Prejde cez iterátor a nechá len prvky, pre ktoré closure vráti `true`. + +**Typ:** `.filter()` dáva referencie na referencie. Ak iteruješ cez `&T`, vo filtri dostaneš `&&T`. + +```rust +let cisla = vec![1, 2, 3, 4, 5, 6]; + +// Nechaj len párne +let parne: Vec<&i32> = cisla.iter().filter(|c| *c % 2 == 0).collect(); +// parne = [&2, &4, &6] + +// Ak chceš Vec (nie referencie), použi .copied() alebo .cloned() +let parne: Vec = cisla.iter().filter(|c| *c % 2 == 0).copied().collect(); +// parne = [2, 4, 6] +``` + +**So štruktúrami:** +```rust +struct Kniha { nazov: String, rok: u16 } + +let knihy = vec![ + Kniha { nazov: "A".into(), rok: 2020 }, + Kniha { nazov: "B".into(), rok: 2015 }, +]; + +// Knihy po roku 2018 +let nove: Vec<&Kniha> = knihy.iter().filter(|k| k.rok > 2018).collect(); +``` + +**Na skúške:** Každá metóda typu `daj_knihy_podla_X` je `filter` + `collect`. + +--- + +### D.3 — find + +**Čo robí:** Vráti **prvý** prvok, pre ktorý closure vráti `true`. Vráti `Option`. + +```rust +let cisla = vec![1, 2, 3, 4, 5]; + +let prve_parne: Option<&i32> = cisla.iter().find(|c| *c % 2 == 0); +// Some(&2) + +let viac_ako_10: Option<&i32> = cisla.iter().find(|c| **c > 10); +// None +``` + +**So štruktúrami:** +```rust +let knihy = vec![ + Kniha { nazov: "Hobbit".into(), rok: 1937 }, + Kniha { nazov: "Duna".into(), rok: 1965 }, +]; + +// Nájdi knihu podľa názvu +let najdena: Option<&Kniha> = knihy.iter().find(|k| k.nazov == "Duna"); +``` + +**Na skúške:** Každá metóda typu `daj_knihu_podla_isbn` je `find`. + +--- + +### D.4 — position + +**Čo robí:** Ako `find`, ale vráti **index** namiesto hodnoty. Vráti `Option`. + +```rust +let mena = vec!["Anna".to_string(), "Boris".to_string(), "Cyril".to_string()]; + +let index: Option = mena.iter().position(|m| m == "Boris"); +// Some(1) +``` + +**Hlavné použitie — odstránenie z Vec:** +```rust +fn odstran(zoznam: &mut Vec, meno: &str) -> Result { + // 1. nájdi index + let pos = zoznam.iter().position(|s| s == meno).ok_or(())?; + // 2. odstráň na tom indexe (remove posunie zvyšné prvky) + Ok(zoznam.remove(pos)) +} +``` + +**Na skúške:** Každá metóda `odstran_knihu`, `odstran_kontakt` atď. je `position` + `remove`. + +--- + +### D.5 — map + +**Čo robí:** Transformuje každý prvok. `[A, B, C]` → `[f(A), f(B), f(C)]`. + +```rust +let cisla = vec![1, 2, 3]; + +// Zdvojnásob každé +let dvojnasobok: Vec = cisla.iter().map(|c| c * 2).collect(); +// [2, 4, 6] + +// Prevod na String +let texty: Vec = cisla.iter().map(|c| c.to_string()).collect(); +// ["1", "2", "3"] + +// Vytiahni pole zo štruktúry +let nazvy: Vec<&String> = knihy.iter().map(|k| &k.nazov).collect(); +``` + +--- + +### D.6 — any a all + +**Čo robia:** Vracajú `bool`. + +```rust +let cisla = vec![1, -2, 3, -4, 5]; + +// any — existuje aspoň jeden, pre ktorý platí? +let ma_zaporne: bool = cisla.iter().any(|c| *c < 0); +// true + +// all — platí pre VŠETKY? +let vsetky_kladne: bool = cisla.iter().all(|c| *c > 0); +// false +``` + +--- + +### D.7 — sum a count + +```rust +let cisla = vec![1, 2, 3, 4, 5]; + +// Súčet — musíš špecifikovať typ +let sucet: i32 = cisla.iter().sum(); +// alebo +let sucet = cisla.iter().sum::(); +// 15 + +// Počet prvkov spĺňajúcich podmienku +let pocet_parnych: usize = cisla.iter().filter(|c| *c % 2 == 0).count(); +// 2 +``` + +--- + +### D.8 — max_by_key a min_by_key + +**Čo robí:** Nájde maximum/minimum podľa kľúča. Vráti `Option`. + +```rust +struct Zamestnanec { meno: String, plat: u32 } + +let zamestnanci = vec![ + Zamestnanec { meno: "Anna".into(), plat: 3000 }, + Zamestnanec { meno: "Boris".into(), plat: 4500 }, + Zamestnanec { meno: "Cyril".into(), plat: 2800 }, +]; + +let najbohatsi: Option<&Zamestnanec> = zamestnanci.iter().max_by_key(|z| z.plat); +// Some(Boris) +``` + +--- + +### D.9 — enumerate + +**Čo robí:** Pridá index ku každému prvku. `[A, B, C]` → `[(0, A), (1, B), (2, C)]`. + +```rust +let mena = vec!["Anna", "Boris", "Cyril"]; + +for (i, meno) in mena.iter().enumerate() { + println!("{}. {}", i + 1, meno); +} +// 1. Anna +// 2. Boris +// 3. Cyril +``` + +--- + +### D.10 — Reťazenie + +Iterátory sa dajú reťaziť. Každá metóda vráti nový iterátor: + +```rust +let zamestnanci = vec![/* ... */]; + +// Mená zamestnancov s platom nad 3000, zoradené +let bohati_mena: Vec<&String> = zamestnanci.iter() + .filter(|z| z.plat > 3000) // nechaj len bohatých + .map(|z| &z.meno) // vytiahni meno + .collect(); // zhromaždi do Vec +``` + +**Kedy for, kedy iterátor:** +- Jednoduchý prechod s výpisom → `for` +- Filtrovanie → `.filter().collect()` +- Transformácia → `.map().collect()` +- Hľadanie → `.find()` alebo `.position()` +- Podmienka → `.any()` alebo `.all()` +- Počítanie → `.filter().count()` +- Ak robíš kombináciu hore → reťaz iterátorov + +--- + +### Úlohy D + +**D-1.** Máš `Vec` = `[3, -1, 4, -1, 5, 9, -2, 6]`. Napíš jedným výrazom (bez for): + - a) Počet kladných čísel + - b) Súčet záporných čísel + - c) Vec len kladných čísel (ako `Vec`, nie referencie) + - d) Či sú všetky čísla väčšie ako -5 + - e) Či existuje číslo väčšie ako 8 + +**D-2.** Máš: +```rust +struct Student { meno: String, vek: u8, priemer: f32 } +let studenti: Vec = /* ... */; +``` +Napíš (každé na jeden riadok): + - a) Mená študentov s priemerom pod 2.0 → `Vec<&String>` + - b) Najstarší študent → `Option<&Student>` + - c) Priemerný vek všetkých študentov → `Option` + - d) Existuje študent mladší ako 18? → `bool` + - e) Odstráň študenta podľa mena z `&mut Vec` → `Result` + +**D-3.** Prepíš na iterátorový zápis bez `for` a bez `mut` premenných: +```rust +fn kladne_zdvojnasobene(cisla: &[i32]) -> Vec { + let mut vysledok = Vec::new(); + for c in cisla { + if *c > 0 { + vysledok.push(c * 2); + } + } + vysledok +} +``` + +**D-4.** Čo je zle? Oprav: +```rust +fn zvys_vsetky(cisla: &mut Vec) { + for c in cisla.iter() { + *c += 1; + } +} +``` + +--- + +## Časť E: Closures (uzávery) + +### E.1 — Čo je closure + +Closure je anonymná funkcia, ktorá môže zachytiť premenné z okolitého prostredia. + +```rust +// Normálna funkcia — nemá prístup k okoliu +fn pricti_5(x: i32) -> i32 { + x + 5 +} + +// Closure — zachytí premennú `zaklad` z okolia +let zaklad = 5; +let pricti = |x: i32| -> i32 { x + zaklad }; + +println!("{}", pricti(10)); // 15 +``` + +### E.2 — Syntax closure + +Od najdlhšieho po najkratšie: + +```rust +// Plný zápis (ako funkcia, ale s ||) +let f = |x: i32, y: i32| -> i32 { x + y }; + +// Bez typov (Rust si ich odvodí) +let f = |x, y| { x + y }; + +// Bez zátvoriek (ak je len jeden výraz) +let f = |x, y| x + y; + +// Bez parametrov +let f = || println!("ahoj"); + +// S jedným parametrom +let f = |x| x * 2; +``` + +**Pravidlo:** V `.filter()`, `.map()`, `.find()` atď. vždy píš najkratší zápis. + +### E.3 — Closures v iterátoroch + +Toto sú najčastejšie situácie: + +```rust +let cisla = vec![1, 2, 3, 4, 5]; + +// filter — closure berie &&T (referenciu na referenciu) +cisla.iter().filter(|c| **c > 3) // c je &&i32, treba ** +cisla.iter().filter(|&c| *c > 3) // destructuring v parametri +cisla.iter().filter(|&&c| c > 3) // plný destructuring + +// map — closure berie &T +cisla.iter().map(|c| c * 2) // c je &i32, Rust automaticky dereferencuje pri * + +// find — rovnako ako filter +cisla.iter().find(|c| **c == 3) + +// any/all — rovnako ako filter +cisla.iter().any(|c| *c > 3) // tu je len &T, nie &&T! +``` + +**Prečo filter má `&&T`?** Lebo `.iter()` dáva `&T`, a filter ešte obalí ďalšou referenciou. Ale Rust je v praxi dosť chytrý — väčšinou stačí `|c| c > &3` alebo `|c| *c > 3`. + +### E.4 — move closure + +Normálne closure zachytáva referencie. `move` prenesie vlastníctvo do closure: + +```rust +// BEZ move — closure zachytí &meno +let meno = "Anna".to_string(); +let pozdrav = || println!("Ahoj {}", meno); +pozdrav(); +println!("{}", meno); // OK, meno stále existuje + +// S move — closure prevezme vlastníctvo mena +let meno = "Anna".to_string(); +let pozdrav = move || println!("Ahoj {}", meno); +pozdrav(); +// println!("{}", meno); // CHYBA! meno bolo presunuté do closure +``` + +**Kedy treba move:** Keď closure musí prežiť pôvodnú premennú (napr. vrátenie closure z funkcie). + +```rust +fn vytvor_pozdrav(meno: String) -> impl Fn() { + // BEZ move: CHYBA — meno zomrie na konci funkcie, ale closure ho referencuje + // S move: OK — closure vlastní meno + move || println!("Ahoj {}", meno) +} +``` + +### E.5 — Closure zachytávajúci &mut + +Ak closure modifikuje premennú, zachytí ju ako `&mut`: + +```rust +let mut pocitadlo = 0; +let mut zvys = || { pocitadlo += 1; }; +zvys(); +zvys(); +zvys(); +println!("{}", pocitadlo); // 3 +``` + +**Dôležité:** Kým existuje `&mut` closure, nemôžeš čítať premennú inak: +```rust +let mut cisla = vec![1, 2, 3]; +let mut pridaj = |x| cisla.push(x); +pridaj(4); +// println!("{:?}", cisla); // CHYBA TU! pridaj stále drží &mut cisla +pridaj(5); +drop(pridaj); // explicitne uvoľni closure +println!("{:?}", cisla); // OK teraz — [1, 2, 3, 4, 5] +``` + +Alebo jednoducho — posledné použitie closure pred prvým iným použitím: +```rust +let mut cisla = vec![1, 2, 3]; +let mut pridaj = |x| cisla.push(x); +pridaj(4); +pridaj(5); // posledné volanie +println!("{:?}", cisla); // OK — Rust vie, že pridaj sa už nepoužije +``` + +--- + +### Úlohy E + +**E-1.** Napíš 3 verzie tej istej operácie (vyfiltruj čísla > 10 z Vec): + - a) S pomenovanou funkciou (nie closure) + - b) S plným zápisom closure (aj typy) + - c) S najkratším zápisom + +**E-2.** Čo je zle? Oprav: +```rust +fn vrat_nasobicku(faktor: i32) -> impl Fn(i32) -> i32 { + |x| x * faktor +} +``` + +**E-3.** Napíš funkciu `aplikuj_na_vsetky(cisla: &[i32], f: impl Fn(i32) -> i32) -> Vec` ktorá aplikuje funkciu `f` na každý prvok. +Potom ju zavolaj s: + - a) closure, ktorý zdvojnásobí + - b) closure, ktorý pripočíta 100 + - c) pomenovanou funkciou, ktorá vráti absolútnu hodnotu + +**E-4.** Máš `Vec`. Napíš closure, ktorý: + - Zachytí `&mut Vec` + - Pridá reťazec do vektora + - Zavolaj ho 3x, potom vypíš výsledok + +--- + +## Časť F: HashMap + +### F.1 — Základy + +```rust +use std::collections::HashMap; + +let mut mapa: HashMap = HashMap::new(); + +// Vloženie +mapa.insert("Anna".to_string(), 25); +mapa.insert("Boris".to_string(), 30); + +// Čítanie — vráti Option<&V> +let vek: Option<&i32> = mapa.get("Anna"); // Some(&25) +let vek: Option<&i32> = mapa.get("Cyril"); // None + +// Kontrola existencie +let existuje: bool = mapa.contains_key("Anna"); // true + +// Iterácia +for (meno, vek) in &mapa { + println!("{}: {}", meno, vek); +} +``` + +### F.2 — Entry API (dôležité na skúške!) + +`entry()` je spôsob, ako pracovať s kľúčom, ktorý môže alebo nemusí existovať: + +```rust +use std::collections::HashMap; + +let mut pocty: HashMap = HashMap::new(); +let slova = vec!["ahoj", "svet", "ahoj", "rust", "svet", "ahoj"]; + +// Počítaj výskyty +for slovo in &slova { + // entry() vráti Entry — buď Occupied alebo Vacant + // or_insert(0) — ak kľúč neexistuje, vlož 0 + // vráti &mut hodnotu + *pocty.entry(slovo.to_string()).or_insert(0) += 1; +} +// pocty = {"ahoj": 3, "svet": 2, "rust": 1} +``` + +**Rozklad:** +```rust +*pocty.entry(slovo.to_string()).or_insert(0) += 1; +// ^^^^^ nájdi alebo vytvor kľúč +// ^^^^^^^^^^^^ ak neexistuje, vlož 0 +// ^^^^ vráti &mut usize +// ^^^ += 1 dereferencuj a zvýš +``` + +**Na skúške:** Metódy ako `vypis_vydavatelstva_a_pocet_knih` alebo `pocet_v_kategoriach` vždy používajú tento vzor. + +### F.3 — HashMap zo štruktúr + +Typický vzor na skúške — zoskup podľa poľa a spočítaj: + +```rust +struct Kniha { nazov: String, vydavatelstvo: String } + +fn pocty_podla_vydavatelstva(knihy: &[Kniha]) -> HashMap { + let mut mapa = HashMap::new(); + for kniha in knihy { + *mapa.entry(kniha.vydavatelstvo.clone()).or_insert(0) += 1; + } + mapa +} + +// Výpis: +fn vypis_statistiky(knihy: &[Kniha]) { + let pocty = pocty_podla_vydavatelstva(knihy); + for (vydavatelstvo, pocet) in &pocty { + println!("{}: {}", vydavatelstvo, pocet); + } +} +``` + +### F.4 — or_insert_with a or_default + +```rust +// or_insert_with — lazy verzia, closure sa zavolá len ak kľúč neexistuje +mapa.entry(kluc).or_insert_with(|| Vec::new()); + +// or_default — použije Default trait +// Pre Vec → prázdny vektor, pre usize → 0, pre String → "" +mapa.entry(kluc).or_default(); + +// Príklad: zoskupenie kníh podľa žánru +let mut podla_zanru: HashMap> = HashMap::new(); +for kniha in &knihy { + podla_zanru.entry(kniha.zaner.clone()).or_default().push(kniha); +} +``` + +--- + +### Úlohy F + +**F-1.** Napíš funkciu `pocitaj_znaky(text: &str) -> HashMap` — počet výskytov každého znaku. + +**F-2.** Máš `Vec` kde Student má `meno`, `trieda: String`, `priemer: f32`. Napíš: + - a) `pocet_v_triedach(studenti: &[Student]) -> HashMap` — koľko študentov v každej triede + - b) `najlepsi_v_triede(studenti: &[Student]) -> HashMap` — študent s najnižším priemerom v každej triede + +**F-3.** Napíš funkciu `invertuj_mapu(mapa: &HashMap) -> HashMap` — otočí kľúče a hodnoty. + +--- + +## Časť G: Option/Result kombinátory + +### G.1 — .map() na Option + +**Čo robí:** Ak je `Some(x)`, aplikuje funkciu na `x`. Ak `None`, zostane `None`. + +```rust +let a: Option = Some(5); +let b: Option = None; + +let a2: Option = a.map(|x| x * 2); // Some(10) +let b2: Option = b.map(|x| x * 2); // None + +// Praktické — vytiahni pole zo Option<&Struct> +let kniha: Option<&Kniha> = najdi_knihu("Duna"); +let nazov: Option<&String> = kniha.map(|k| &k.nazov); + +// Prevod typu +let cislo: Option = Some(42); +let text: Option = cislo.map(|c| c.to_string()); // Some("42") +``` + +**Nahrádza:** +```rust +// Toto: +match opt { + Some(x) => Some(f(x)), + None => None, +} +// Je to isté ako: +opt.map(f) +``` + +### G.2 — .and_then() (flatmap) + +**Čo robí:** Ako `.map()`, ale closure vracia `Option`. Zabraňuje `Option>`. + +```rust +// map by vytvorilo Option>: +let text: Option = Some("42".to_string()); +let zle: Option> = text.map(|t| t.parse::().ok()); + +// and_then "sploští": +let text: Option = Some("42".to_string()); +let dobre: Option = text.and_then(|t| t.parse::().ok()); +// Some(42) + +let text2: Option = Some("abc".to_string()); +let dobre2: Option = text2.and_then(|t| t.parse::().ok()); +// None (parse zlyhalo) +``` + +**Pravidlo:** Ak tvoja closure vracia `Option` → použi `.and_then()`. Ak vracia holú hodnotu → použi `.map()`. + +### G.3 — .map_err() + +**Čo robí:** Transformuje chybu v `Result`, hodnotu necháva. + +```rust +// read_to_string vracia Result +// Ty chceš Result +fn nacitaj(cesta: &str) -> Result { + std::fs::read_to_string(cesta) + .map_err(|e| format!("Chyba čítania: {}", e)) +} +``` + +### G.4 — .flatten() + +**Čo robí:** `Option>` → `Option`, `Result, E>` → `Result`. + +```rust +let a: Option> = Some(Some(5)); +let b: Option> = Some(None); +let c: Option> = None; + +a.flatten() // Some(5) +b.flatten() // None +c.flatten() // None +``` + +### G.5 — .unwrap_or(), .unwrap_or_default(), .unwrap_or_else() + +```rust +let a: Option = None; + +a.unwrap_or(0) // 0 — vždy vyhodnotí fallback +a.unwrap_or_default() // 0 — použije Default trait (i32 default = 0) +a.unwrap_or_else(|| { // lazy — closure sa zavolá len ak None + println!("Počítam fallback..."); + vypocitaj_nieco() +}) +``` + +**Kedy ktorý:** +- `.unwrap_or(hodnota)` — fallback je jednoduchá konštanta +- `.unwrap_or_default()` — chceš default pre daný typ (0, "", Vec::new(), false) +- `.unwrap_or_else(|| ...)` — fallback je drahý výpočet + +--- + +### Úlohy G + +**G-1.** Prepíš bez `match`: +```rust +fn zdvojnasob_option(x: Option) -> Option { + match x { + Some(n) => Some(n * 2), + None => None, + } +} +``` + +**G-2.** Prepíš bez `match`: +```rust +fn parsuj_prvy_znak(text: Option) -> Option { + match text { + Some(t) => { + match t.chars().next() { + Some(c) => Some(c), + None => None, + } + } + None => None, + } +} +``` + +**G-3.** Napíš funkciu `bezpecne_delenie(a: f64, b: f64) -> Option` — vráti `None` ak `b == 0.0`. Bez `if`, použi `.filter()` alebo podmienku v `.and_then()`. + +Potom napíš: `delenie_s_fallbackom(a: f64, b: f64) -> f64` — ak sa nedá deliť, vráti `0.0`. Použi predošlú funkciu + `.unwrap_or()`. + +**G-4.** Máš: +```rust +fn najdi_pouzivatela(id: u32) -> Option { /* ... */ } +fn najdi_email(meno: &str) -> Option { /* ... */ } +``` +Napíš funkciu `email_pre_id(id: u32) -> Option` — nájdi použivateľa podľa id, potom jeho email. Jeden riadok s `.and_then()`. + +**G-5.** Čo vypíše? Najprv tipni, potom over: +```rust +let a: Option = Some(5); +println!("{:?}", a.map(|x| x > 3)); +println!("{:?}", a.filter(|x| *x > 3)); +println!("{:?}", a.filter(|x| *x > 10)); +println!("{:?}", a.map(|x| x.to_string())); +println!("{:?}", a.and_then(|x| if x > 3 { Some(x * 2) } else { None })); +``` + +--- + +## Časť H: Kombinované cvičenia (simulácia skúšky) + +### H-1. Kompletný CRUD + +Implementuj bez pozerania na čokoľvek: + +```rust +use serde::{Serialize, Deserialize}; +use std::collections::HashMap; + +#[derive(Serialize, Deserialize, Clone)] +struct Produkt { + nazov: String, + cena: f64, + kategoria: String, + na_sklade: bool, +} + +#[derive(Serialize, Deserialize, Default)] +struct Obchod { + produkty: Vec, +} + +impl Obchod { + fn nacitaj_zo_suboru(cesta: &std::path::PathBuf) -> Option { todo!() } + fn uloz_do_suboru(&self, cesta: &std::path::PathBuf) -> bool { todo!() } + + fn pridaj(&mut self, produkt: Produkt) -> Result<(), ()> { todo!() } + // duplicita podľa nazvu + + fn odstran(&mut self, nazov: &str) -> Result { todo!() } + + fn najdi(&self, nazov: &str) -> Option<&Produkt> { todo!() } + + fn na_sklade(&self) -> Vec<&Produkt> { todo!() } + + fn v_kategorii(&self, kat: &str) -> Vec<&Produkt> { todo!() } + + fn najdrahsi(&self) -> Option<&Produkt> { todo!() } + + fn priemerny_cena_v_kategorii(&self, kat: &str) -> Option { todo!() } + + fn pocty_kategorii(&self) -> HashMap { todo!() } +} +``` + +### H-2. Display enum + struct + +```rust +use std::fmt; + +enum Velkost { Mala, Stredna, Velka } +// Implementuj Display: "S", "M", "L" + +struct Tricko { farba: String, velkost: Velkost, cena: f64 } +// Implementuj Display: "Tričko [M] červené - 19.99€" +``` + +--- + +--- + +--- + +# RIEŠENIA + +## D-1 + +```rust +let cisla = vec![3, -1, 4, -1, 5, 9, -2, 6]; + +// a) +let pocet_kladnych: usize = cisla.iter().filter(|c| **c > 0).count(); +// 5 + +// b) +let sucet_zapornych: i32 = cisla.iter().filter(|c| **c < 0).sum(); +// -4 + +// c) +let kladne: Vec = cisla.iter().filter(|c| **c > 0).copied().collect(); +// [3, 4, 5, 9, 6] + +// d) +let vsetky_nad_minus5: bool = cisla.iter().all(|c| *c > -5); +// true + +// e) +let existuje_nad_8: bool = cisla.iter().any(|c| *c > 8); +// true +``` + +## D-2 + +```rust +struct Student { meno: String, vek: u8, priemer: f32 } + +// a) +let mena: Vec<&String> = studenti.iter().filter(|s| s.priemer < 2.0).map(|s| &s.meno).collect(); + +// b) +let najstarsi: Option<&Student> = studenti.iter().max_by_key(|s| s.vek); + +// c) +fn priemerny_vek(studenti: &[Student]) -> Option { + if studenti.is_empty() { return None; } + let sucet: u32 = studenti.iter().map(|s| s.vek as u32).sum(); + Some(sucet as f64 / studenti.len() as f64) +} + +// d) +let ma_mladsiho: bool = studenti.iter().any(|s| s.vek < 18); + +// e) +fn odstran_studenta(studenti: &mut Vec, meno: &str) -> Result { + let pos = studenti.iter().position(|s| s.meno == meno).ok_or(())?; + Ok(studenti.remove(pos)) +} +``` + +## D-3 + +```rust +fn kladne_zdvojnasobene(cisla: &[i32]) -> Vec { + cisla.iter().filter(|c| **c > 0).map(|c| c * 2).collect() +} +``` + +## D-4 + +`iter()` dáva `&i32` (nemeniteľná referencia). Nemôžeš cez ňu meniť. Treba `iter_mut()`: + +```rust +fn zvys_vsetky(cisla: &mut Vec) { + for c in cisla.iter_mut() { + *c += 1; + } +} +``` + +## E-1 + +```rust +let cisla = vec![1, 5, 12, 3, 15, 8]; + +// a) pomenovaná funkcia +fn je_viac_ako_10(c: &&i32) -> bool { **c > 10 } +let vysledok: Vec<&i32> = cisla.iter().filter(je_viac_ako_10).collect(); + +// b) plný zápis +let vysledok: Vec<&i32> = cisla.iter().filter(|c: &&i32| -> bool { **c > 10 }).collect(); + +// c) najkratší +let vysledok: Vec<&i32> = cisla.iter().filter(|c| **c > 10).collect(); +``` + +## E-2 + +Treba `move` — `faktor` je lokálna premenná, closure ho musí vlastniť: + +```rust +fn vrat_nasobicku(faktor: i32) -> impl Fn(i32) -> i32 { + move |x| x * faktor +} +``` + +## E-3 + +```rust +fn aplikuj_na_vsetky(cisla: &[i32], f: impl Fn(i32) -> i32) -> Vec { + cisla.iter().map(|c| f(*c)).collect() +} + +fn main() { + let cisla = vec![1, -2, 3, -4, 5]; + + // a) + let a = aplikuj_na_vsetky(&cisla, |x| x * 2); + + // b) + let b = aplikuj_na_vsetky(&cisla, |x| x + 100); + + // c) + fn absolutna(x: i32) -> i32 { x.abs() } + let c = aplikuj_na_vsetky(&cisla, absolutna); +} +``` + +## E-4 + +```rust +fn main() { + let mut zoznam = Vec::new(); + let mut pridaj = |s: &str| zoznam.push(s.to_string()); + pridaj("ahoj"); + pridaj("svet"); + pridaj("rust"); + println!("{:?}", zoznam); // ["ahoj", "svet", "rust"] +} +``` + +## F-1 + +```rust +fn pocitaj_znaky(text: &str) -> HashMap { + let mut mapa = HashMap::new(); + for c in text.chars() { + *mapa.entry(c).or_insert(0) += 1; + } + mapa +} +``` + +## F-2 + +```rust +struct Student { meno: String, trieda: String, priemer: f32 } + +// a) +fn pocet_v_triedach(studenti: &[Student]) -> HashMap { + let mut mapa = HashMap::new(); + for s in studenti { + *mapa.entry(s.trieda.clone()).or_insert(0) += 1; + } + mapa +} + +// b) +fn najlepsi_v_triede<'a>(studenti: &'a [Student]) -> HashMap { + let mut mapa: HashMap = HashMap::new(); + for s in studenti { + mapa.entry(s.trieda.clone()) + .and_modify(|najlepsi| { + if s.priemer < najlepsi.priemer { + *najlepsi = s; + } + }) + .or_insert(s); + } + mapa +} +``` + +## F-3 + +```rust +fn invertuj_mapu(mapa: &HashMap) -> HashMap { + let mut nova = HashMap::new(); + for (k, v) in mapa { + nova.insert(v.clone(), k.clone()); + } + nova +} +``` + +## G-1 + +```rust +fn zdvojnasob_option(x: Option) -> Option { + x.map(|n| n * 2) +} +``` + +## G-2 + +```rust +fn parsuj_prvy_znak(text: Option) -> Option { + text.and_then(|t| t.chars().next()) +} +``` + +## G-3 + +```rust +fn bezpecne_delenie(a: f64, b: f64) -> Option { + Some(b).filter(|b| *b != 0.0).map(|b| a / b) +} + +fn delenie_s_fallbackom(a: f64, b: f64) -> f64 { + bezpecne_delenie(a, b).unwrap_or(0.0) +} +``` + +## G-4 + +```rust +fn email_pre_id(id: u32) -> Option { + najdi_pouzivatela(id).and_then(|meno| najdi_email(&meno)) +} +``` + +## G-5 + +``` +Some(true) // map: 5 > 3 = true, zabalí do Some +Some(5) // filter: 5 > 3 je true, nechá Some(5) +None // filter: 5 > 10 je false, vráti None +Some("5") // map: 5.to_string() = "5" +Some(10) // and_then: 5 > 3, tak Some(5 * 2) = Some(10) +``` + +## H-1 + +```rust +use serde::{Serialize, Deserialize}; +use std::collections::HashMap; + +#[derive(Serialize, Deserialize, Clone)] +struct Produkt { + nazov: String, + cena: f64, + kategoria: String, + na_sklade: bool, +} + +#[derive(Serialize, Deserialize, Default)] +struct Obchod { + produkty: Vec, +} + +impl Obchod { + fn nacitaj_zo_suboru(cesta: &std::path::PathBuf) -> Option { + let raw = std::fs::read_to_string(cesta).ok()?; + serde_json::from_str(&raw).ok() + } + + fn uloz_do_suboru(&self, cesta: &std::path::PathBuf) -> bool { + let Ok(json) = serde_json::to_string_pretty(&self) else { + return false; + }; + std::fs::write(cesta, json).is_ok() + } + + fn pridaj(&mut self, produkt: Produkt) -> Result<(), ()> { + if self.produkty.iter().any(|p| p.nazov == produkt.nazov) { + return Err(()); + } + self.produkty.push(produkt); + Ok(()) + } + + fn odstran(&mut self, nazov: &str) -> Result { + let pos = self.produkty.iter().position(|p| p.nazov == nazov).ok_or(())?; + Ok(self.produkty.remove(pos)) + } + + fn najdi(&self, nazov: &str) -> Option<&Produkt> { + self.produkty.iter().find(|p| p.nazov == nazov) + } + + fn na_sklade(&self) -> Vec<&Produkt> { + self.produkty.iter().filter(|p| p.na_sklade).collect() + } + + fn v_kategorii(&self, kat: &str) -> Vec<&Produkt> { + self.produkty.iter().filter(|p| p.kategoria == kat).collect() + } + + fn najdrahsi(&self) -> Option<&Produkt> { + self.produkty.iter().max_by(|a, b| a.cena.partial_cmp(&b.cena).unwrap()) + } + + fn priemerna_cena_v_kategorii(&self, kat: &str) -> Option { + let v_kat: Vec<&Produkt> = self.v_kategorii(kat); + if v_kat.is_empty() { return None; } + let sucet: f64 = v_kat.iter().map(|p| p.cena).sum(); + Some(sucet / v_kat.len() as f64) + } + + fn pocty_kategorii(&self) -> HashMap { + let mut mapa = HashMap::new(); + for p in &self.produkty { + *mapa.entry(p.kategoria.clone()).or_insert(0) += 1; + } + mapa + } +} +``` + +## H-2 + +```rust +use std::fmt; + +enum Velkost { Mala, Stredna, Velka } + +impl fmt::Display for Velkost { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Velkost::Mala => write!(f, "S"), + Velkost::Stredna => write!(f, "M"), + Velkost::Velka => write!(f, "L"), + } + } +} + +struct Tricko { farba: String, velkost: Velkost, cena: f64 } + +impl fmt::Display for Tricko { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Tričko [{}] {} - {:.2}€", self.velkost, self.farba, self.cena) + } +} +```