Files
JR-priprava-na-skusku/priprava/rust_priprava3.md
2026-02-16 19:50:35 +01:00

1120 lines
28 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Rust — Iterátory, Closures, Kombinácie (DG)
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<i32> (nie referencie), použi .copied() alebo .cloned()
let parne: Vec<i32> = 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<usize>`.
```rust
let mena = vec!["Anna".to_string(), "Boris".to_string(), "Cyril".to_string()];
let index: Option<usize> = mena.iter().position(|m| m == "Boris");
// Some(1)
```
**Hlavné použitie — odstránenie z Vec:**
```rust
fn odstran(zoznam: &mut Vec<String>, meno: &str) -> Result<String, ()> {
// 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<i32> = cisla.iter().map(|c| c * 2).collect();
// [2, 4, 6]
// Prevod na String
let texty: Vec<String> = 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::<i32>();
// 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<i32>` = `[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<i32>`, 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<Student> = /* ... */;
```
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<f64>`
- d) Existuje študent mladší ako 18? → `bool`
- e) Odstráň študenta podľa mena z `&mut Vec<Student>``Result<Student, ()>`
**D-3.** Prepíš na iterátorový zápis bez `for` a bez `mut` premenných:
```rust
fn kladne_zdvojnasobene(cisla: &[i32]) -> Vec<i32> {
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<i32>) {
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<i32>):
- 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<i32>` 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<String>`. Napíš closure, ktorý:
- Zachytí `&mut Vec<String>`
- 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<String, i32> = 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<String, usize> = 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<String, usize> {
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<String, Vec<&Kniha>> = 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<char, usize>` — počet výskytov každého znaku.
**F-2.** Máš `Vec<Student>` kde Student má `meno`, `trieda: String`, `priemer: f32`. Napíš:
- a) `pocet_v_triedach(studenti: &[Student]) -> HashMap<String, usize>` — koľko študentov v každej triede
- b) `najlepsi_v_triede(studenti: &[Student]) -> HashMap<String, &Student>` — študent s najnižším priemerom v každej triede
**F-3.** Napíš funkciu `invertuj_mapu(mapa: &HashMap<String, String>) -> HashMap<String, String>` — 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<i32> = Some(5);
let b: Option<i32> = None;
let a2: Option<i32> = a.map(|x| x * 2); // Some(10)
let b2: Option<i32> = 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<i32> = Some(42);
let text: Option<String> = 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<Option<T>>`.
```rust
// map by vytvorilo Option<Option<i32>>:
let text: Option<String> = Some("42".to_string());
let zle: Option<Option<i32>> = text.map(|t| t.parse::<i32>().ok());
// and_then "sploští":
let text: Option<String> = Some("42".to_string());
let dobre: Option<i32> = text.and_then(|t| t.parse::<i32>().ok());
// Some(42)
let text2: Option<String> = Some("abc".to_string());
let dobre2: Option<i32> = text2.and_then(|t| t.parse::<i32>().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<String, io::Error>
// Ty chceš Result<String, String>
fn nacitaj(cesta: &str) -> Result<String, String> {
std::fs::read_to_string(cesta)
.map_err(|e| format!("Chyba čítania: {}", e))
}
```
### G.4 — .flatten()
**Čo robí:** `Option<Option<T>>``Option<T>`, `Result<Result<T, E>, E>``Result<T, E>`.
```rust
let a: Option<Option<i32>> = Some(Some(5));
let b: Option<Option<i32>> = Some(None);
let c: Option<Option<i32>> = 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<i32> = 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<i32>) -> Option<i32> {
match x {
Some(n) => Some(n * 2),
None => None,
}
}
```
**G-2.** Prepíš bez `match`:
```rust
fn parsuj_prvy_znak(text: Option<String>) -> Option<char> {
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<f64>` — 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<String> { /* ... */ }
fn najdi_email(meno: &str) -> Option<String> { /* ... */ }
```
Napíš funkciu `email_pre_id(id: u32) -> Option<String>` — 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<i32> = 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<Produkt>,
}
impl Obchod {
fn nacitaj_zo_suboru(cesta: &std::path::PathBuf) -> Option<Obchod> { 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<Produkt, ()> { 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<f64> { todo!() }
fn pocty_kategorii(&self) -> HashMap<String, usize> { 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<i32> = 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<f64> {
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<Student>, meno: &str) -> Result<Student, ()> {
let pos = studenti.iter().position(|s| s.meno == meno).ok_or(())?;
Ok(studenti.remove(pos))
}
```
## D-3
```rust
fn kladne_zdvojnasobene(cisla: &[i32]) -> Vec<i32> {
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<i32>) {
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<i32> {
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<char, usize> {
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<String, usize> {
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<String, &'a Student> {
let mut mapa: HashMap<String, &Student> = 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<String, String>) -> HashMap<String, String> {
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<i32>) -> Option<i32> {
x.map(|n| n * 2)
}
```
## G-2
```rust
fn parsuj_prvy_znak(text: Option<String>) -> Option<char> {
text.and_then(|t| t.chars().next())
}
```
## G-3
```rust
fn bezpecne_delenie(a: f64, b: f64) -> Option<f64> {
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<String> {
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<Produkt>,
}
impl Obchod {
fn nacitaj_zo_suboru(cesta: &std::path::PathBuf) -> Option<Obchod> {
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<Produkt, ()> {
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<f64> {
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<String, usize> {
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)
}
}
```